Przeglądaj źródła

cache service refactoring

Jason Rivard 7 lat temu
rodzic
commit
5c9c9e1fe4

+ 2 - 2
server/src/main/java/password/pwm/bean/UserIdentity.java

@@ -103,8 +103,8 @@ public class UserIdentity implements Serializable, Comparable
 
 
         // check app cache.  This is used primarily so that keys are static over some meaningful lifetime, allowing browser caching based on keys.
         // check app cache.  This is used primarily so that keys are static over some meaningful lifetime, allowing browser caching based on keys.
         final CacheService cacheService = pwmApplication.getCacheService();
         final CacheService cacheService = pwmApplication.getCacheService();
-        final CacheKey cacheKey = CacheKey.makeCacheKey( this.getClass(), null, "userKey" + "|" + this.toDelimitedKey() );
-        final String cachedValue = cacheService.get( cacheKey );
+        final CacheKey cacheKey = CacheKey.newKey( this.getClass(), this, "obfuscatedKey" );
+        final String cachedValue = cacheService.get( cacheKey, String.class );
 
 
         if ( !StringUtil.isEmpty( cachedValue ) )
         if ( !StringUtil.isEmpty( cachedValue ) )
         {
         {

+ 2 - 2
server/src/main/java/password/pwm/config/profile/LdapProfile.java

@@ -146,10 +146,10 @@ public class LdapProfile extends AbstractProfile implements Profile
         final boolean enableCanonicalCache = Boolean.parseBoolean( pwmApplication.getConfig().readAppProperty( AppProperty.LDAP_CACHE_CANONICAL_ENABLE ) );
         final boolean enableCanonicalCache = Boolean.parseBoolean( pwmApplication.getConfig().readAppProperty( AppProperty.LDAP_CACHE_CANONICAL_ENABLE ) );
 
 
         String canonicalValue = null;
         String canonicalValue = null;
-        final CacheKey cacheKey = CacheKey.makeCacheKey( LdapPermissionTester.class, null, "canonicalDN-" + this.getIdentifier() + "-" + dnValue );
+        final CacheKey cacheKey = CacheKey.newKey( LdapPermissionTester.class, null, "canonicalDN-" + this.getIdentifier() + "-" + dnValue );
         if ( enableCanonicalCache )
         if ( enableCanonicalCache )
         {
         {
-            final String cachedDN = pwmApplication.getCacheService().get( cacheKey );
+            final String cachedDN = pwmApplication.getCacheService().get( cacheKey, String.class );
             if ( cachedDN != null )
             if ( cachedDN != null )
             {
             {
                 canonicalValue = cachedDN;
                 canonicalValue = cachedDN;

+ 10 - 11
server/src/main/java/password/pwm/http/servlet/peoplesearch/PeopleSearchDataReader.java

@@ -95,13 +95,12 @@ class PeopleSearchDataReader
 
 
         {
         {
             // try to serve from cache first
             // try to serve from cache first
-            final String cachedOutput = pwmRequest.getPwmApplication().getCacheService().get( cacheKey );
-            if ( cachedOutput != null )
+            final SearchResultBean cachedResult = pwmRequest.getPwmApplication().getCacheService().get( cacheKey, SearchResultBean.class );
+            if ( cachedResult != null )
             {
             {
-                final SearchResultBean searchResultBean = JsonUtil.deserialize( cachedOutput, SearchResultBean.class );
-                searchResultBean.setFromCache( true );
+                cachedResult.setFromCache( true );
                 StatisticsManager.incrementStat( pwmRequest, Statistic.PEOPLESEARCH_CACHE_HITS );
                 StatisticsManager.incrementStat( pwmRequest, Statistic.PEOPLESEARCH_CACHE_HITS );
-                return searchResultBean;
+                return cachedResult;
             }
             }
             else
             else
             {
             {
@@ -134,12 +133,12 @@ class PeopleSearchDataReader
 
 
         {
         {
             // if value is cached then return;
             // if value is cached then return;
-            final String cachedOutput = pwmRequest.getPwmApplication().getCacheService().get( cacheKey );
+            final OrgChartDataBean cachedOutput = pwmRequest.getPwmApplication().getCacheService().get( cacheKey, OrgChartDataBean.class );
             if ( cachedOutput != null )
             if ( cachedOutput != null )
             {
             {
                 StatisticsManager.incrementStat( pwmRequest, Statistic.PEOPLESEARCH_CACHE_HITS );
                 StatisticsManager.incrementStat( pwmRequest, Statistic.PEOPLESEARCH_CACHE_HITS );
                 LOGGER.trace( pwmRequest, "completed makeOrgChartData of " + userIdentity.toDisplayString() + " from cache" );
                 LOGGER.trace( pwmRequest, "completed makeOrgChartData of " + userIdentity.toDisplayString() + " from cache" );
-                return JsonUtil.deserialize( cachedOutput, OrgChartDataBean.class );
+                return cachedOutput;
             }
             }
             else
             else
             {
             {
@@ -218,11 +217,11 @@ class PeopleSearchDataReader
 
 
         final CacheKey cacheKey = makeCacheKey( UserDetailBean.class.getSimpleName(), userIdentity.toDelimitedKey() );
         final CacheKey cacheKey = makeCacheKey( UserDetailBean.class.getSimpleName(), userIdentity.toDelimitedKey() );
         {
         {
-            final String cachedOutput = pwmRequest.getPwmApplication().getCacheService().get( cacheKey );
+            final UserDetailBean cachedOutput = pwmRequest.getPwmApplication().getCacheService().get( cacheKey, UserDetailBean.class );
             if ( cachedOutput != null )
             if ( cachedOutput != null )
             {
             {
                 StatisticsManager.incrementStat( pwmRequest, Statistic.PEOPLESEARCH_CACHE_HITS );
                 StatisticsManager.incrementStat( pwmRequest, Statistic.PEOPLESEARCH_CACHE_HITS );
-                return JsonUtil.deserialize( cachedOutput, UserDetailBean.class );
+                return cachedOutput;
             }
             }
             else
             else
             {
             {
@@ -354,7 +353,7 @@ class PeopleSearchDataReader
             userIdentity = null;
             userIdentity = null;
         }
         }
         final String keyString = operationIdentifier + "|" + pwmRequest.getPwmApplication().getSecureService().hash( dataIdentifier );
         final String keyString = operationIdentifier + "|" + pwmRequest.getPwmApplication().getSecureService().hash( dataIdentifier );
-        return CacheKey.makeCacheKey(
+        return CacheKey.newKey(
                 this.getClass(),
                 this.getClass(),
                 userIdentity,
                 userIdentity,
                 keyString );
                 keyString );
@@ -449,7 +448,7 @@ class PeopleSearchDataReader
         if ( maxCacheSeconds > 0 )
         if ( maxCacheSeconds > 0 )
         {
         {
             final CachePolicy cachePolicy = CachePolicy.makePolicyWithExpirationMS( maxCacheSeconds * 1000 );
             final CachePolicy cachePolicy = CachePolicy.makePolicyWithExpirationMS( maxCacheSeconds * 1000 );
-            pwmApplication.getCacheService().put( cacheKey, cachePolicy, JsonUtil.serialize( data ) );
+            pwmApplication.getCacheService().put( cacheKey, cachePolicy, data );
         }
         }
     }
     }
 
 

+ 2 - 2
server/src/main/java/password/pwm/ldap/LdapOperationsHelper.java

@@ -203,11 +203,11 @@ public class LdapOperationsHelper
             throws ChaiUnavailableException, PwmUnrecoverableException
             throws ChaiUnavailableException, PwmUnrecoverableException
     {
     {
         final boolean enableCache = Boolean.parseBoolean( pwmApplication.getConfig().readAppProperty( AppProperty.LDAP_CACHE_USER_GUID_ENABLE ) );
         final boolean enableCache = Boolean.parseBoolean( pwmApplication.getConfig().readAppProperty( AppProperty.LDAP_CACHE_USER_GUID_ENABLE ) );
-        final CacheKey cacheKey = CacheKey.makeCacheKey( LdapOperationsHelper.class, null, "guidValue-" + userIdentity.toDelimitedKey() );
+        final CacheKey cacheKey = CacheKey.newKey( LdapOperationsHelper.class, userIdentity, "guidValue" );
 
 
         if ( enableCache )
         if ( enableCache )
         {
         {
-            final String cachedValue = pwmApplication.getCacheService().get( cacheKey );
+            final String cachedValue = pwmApplication.getCacheService().get( cacheKey, String.class );
             if ( cachedValue != null )
             if ( cachedValue != null )
             {
             {
                 return NULL_CACHE_GUID.equals( cachedValue )
                 return NULL_CACHE_GUID.equals( cachedValue )

+ 1 - 1
server/src/main/java/password/pwm/svc/cache/CacheDebugItem.java

@@ -29,7 +29,7 @@ import java.io.Serializable;
 @Value
 @Value
 class CacheDebugItem implements Serializable
 class CacheDebugItem implements Serializable
 {
 {
-    private String key;
+    private CacheKey cacheKey;
     private String age;
     private String age;
     private int chars;
     private int chars;
 }
 }

+ 14 - 79
server/src/main/java/password/pwm/svc/cache/CacheKey.java

@@ -22,100 +22,35 @@
 
 
 package password.pwm.svc.cache;
 package password.pwm.svc.cache;
 
 
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
+import lombok.Value;
 import password.pwm.bean.UserIdentity;
 import password.pwm.bean.UserIdentity;
-import password.pwm.error.PwmUnrecoverableException;
-import password.pwm.util.secure.PwmHashAlgorithm;
-import password.pwm.util.secure.SecureEngine;
 
 
 import java.io.Serializable;
 import java.io.Serializable;
+import java.util.Objects;
 
 
+@AllArgsConstructor( access = AccessLevel.PRIVATE )
+@Value
 public class CacheKey implements Serializable
 public class CacheKey implements Serializable
 {
 {
-    private final String cacheKey;
-    private transient String hash;
+    final Class srcClass;
+    final UserIdentity userIdentity;
+    final String valueID;
 
 
-    private CacheKey( final String cacheKey )
-    {
-        if ( cacheKey == null )
-        {
-            throw new NullPointerException( "key can not be null" );
-        }
-        this.cacheKey = cacheKey;
-    }
-
-    String getHash( )
-            throws PwmUnrecoverableException
-    {
-        if ( hash != null )
-        {
-            return hash;
-        }
-        hash = SecureEngine.hash( this.cacheKey, PwmHashAlgorithm.SHA256 );
-        return hash;
-    }
-
-    String getStorageValue( )
-    {
-        return cacheKey;
-
-    }
-
-    static CacheKey fromStorageValue( final String input )
-    {
-        return new CacheKey( input );
-    }
-
-    public static CacheKey makeCacheKey(
+    public static CacheKey newKey(
             final Class srcClass,
             final Class srcClass,
             final UserIdentity userIdentity,
             final UserIdentity userIdentity,
             final String valueID
             final String valueID
     )
     )
     {
     {
-        if ( srcClass == null )
-        {
-            throw new NullPointerException( "srcClass can not be null" );
-        }
-        if ( valueID == null )
-        {
-            throw new NullPointerException( "valueID can not be null" );
-        }
+        Objects.requireNonNull( srcClass, "srcClass can not be null" );
+        Objects.requireNonNull( valueID, "valueID can not be null" );
+
         if ( valueID.isEmpty() )
         if ( valueID.isEmpty() )
         {
         {
             throw new IllegalArgumentException( "valueID can not be empty" );
             throw new IllegalArgumentException( "valueID can not be empty" );
         }
         }
-        return new CacheKey( srcClass.getName() + "!" + ( userIdentity == null ? "null" : userIdentity.toDelimitedKey() ) + "!" + valueID );
-    }
-
-    @Override
-    public boolean equals( final Object o )
-    {
-        if ( this == o )
-        {
-            return true;
-        }
-        if ( o == null || getClass() != o.getClass() )
-        {
-            return false;
-        }
-
-        final CacheKey cacheKey1 = ( CacheKey ) o;
-
-        if ( !cacheKey.equals( cacheKey1.cacheKey ) )
-        {
-            return false;
-        }
-
-        return true;
-    }
-
-    @Override
-    public int hashCode( )
-    {
-        return cacheKey.hashCode();
-    }
-
-    public String toString( )
-    {
-        return cacheKey;
+        return new CacheKey( srcClass, userIdentity, valueID );
     }
     }
 }
 }

+ 0 - 7
server/src/main/java/password/pwm/svc/cache/CachePolicy.java

@@ -40,13 +40,6 @@ public class CachePolicy implements Serializable
         return expiration;
         return expiration;
     }
     }
 
 
-    public static CachePolicy makePolicy( final Instant date )
-    {
-        final CachePolicy policy = new CachePolicy();
-        policy.expiration = date;
-        return policy;
-    }
-
     public static CachePolicy makePolicyWithExpirationMS( final long expirationMs )
     public static CachePolicy makePolicyWithExpirationMS( final long expirationMs )
     {
     {
         final CachePolicy policy = new CachePolicy();
         final CachePolicy policy = new CachePolicy();

+ 5 - 29
server/src/main/java/password/pwm/svc/cache/CacheService.java

@@ -31,7 +31,6 @@ import password.pwm.health.HealthRecord;
 import password.pwm.svc.PwmService;
 import password.pwm.svc.PwmService;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.TimeDuration;
 import password.pwm.util.java.TimeDuration;
-import password.pwm.util.localdb.LocalDB;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.logging.PwmLogger;
 
 
 import java.io.Serializable;
 import java.io.Serializable;
@@ -47,7 +46,6 @@ public class CacheService implements PwmService
     private static final PwmLogger LOGGER = PwmLogger.forClass( CacheService.class );
     private static final PwmLogger LOGGER = PwmLogger.forClass( CacheService.class );
 
 
     private MemoryCacheStore memoryCacheStore;
     private MemoryCacheStore memoryCacheStore;
-    private LocalDBCacheStore localDBCacheStore;
 
 
     private STATUS status = STATUS.NEW;
     private STATUS status = STATUS.NEW;
 
 
@@ -87,10 +85,6 @@ public class CacheService implements PwmService
 
 
         status = STATUS.OPENING;
         status = STATUS.OPENING;
         final int maxMemItems = Integer.parseInt( pwmApplication.getConfig().readAppProperty( AppProperty.CACHE_MEMORY_MAX_ITEMS ) );
         final int maxMemItems = Integer.parseInt( pwmApplication.getConfig().readAppProperty( AppProperty.CACHE_MEMORY_MAX_ITEMS ) );
-        if ( pwmApplication.getLocalDB() != null && pwmApplication.getLocalDB().status() == LocalDB.Status.OPEN )
-        {
-            localDBCacheStore = new LocalDBCacheStore( pwmApplication );
-        }
         memoryCacheStore = new MemoryCacheStore( maxMemItems );
         memoryCacheStore = new MemoryCacheStore( maxMemItems );
         status = STATUS.OPEN;
         status = STATUS.OPEN;
     }
     }
@@ -99,7 +93,6 @@ public class CacheService implements PwmService
     public void close( )
     public void close( )
     {
     {
         status = STATUS.CLOSED;
         status = STATUS.CLOSED;
-        localDBCacheStore = null;
     }
     }
 
 
     @Override
     @Override
@@ -119,12 +112,10 @@ public class CacheService implements PwmService
         final Map<String, Serializable> debugInfo = new LinkedHashMap<>( );
         final Map<String, Serializable> debugInfo = new LinkedHashMap<>( );
         debugInfo.put( "memory-statistics", memoryCacheStore.getCacheStoreInfo() );
         debugInfo.put( "memory-statistics", memoryCacheStore.getCacheStoreInfo() );
         debugInfo.put( "memory-items", new ArrayList<Serializable>( memoryCacheStore.getCacheDebugItems() ) );
         debugInfo.put( "memory-items", new ArrayList<Serializable>( memoryCacheStore.getCacheDebugItems() ) );
-        debugInfo.put( "localdb-statistics", localDBCacheStore.getCacheStoreInfo() );
-        debugInfo.put( "localdb-items", new ArrayList<Serializable>( localDBCacheStore.getCacheDebugItems() ) );
         return Collections.unmodifiableMap( debugInfo );
         return Collections.unmodifiableMap( debugInfo );
     }
     }
 
 
-    public void put( final CacheKey cacheKey, final CachePolicy cachePolicy, final String payload )
+    public void put( final CacheKey cacheKey, final CachePolicy cachePolicy, final Serializable payload )
             throws PwmUnrecoverableException
             throws PwmUnrecoverableException
     {
     {
         if ( status != STATUS.OPEN )
         if ( status != STATUS.OPEN )
@@ -145,14 +136,10 @@ public class CacheService implements PwmService
         }
         }
         final Instant expirationDate = cachePolicy.getExpiration();
         final Instant expirationDate = cachePolicy.getExpiration();
         memoryCacheStore.store( cacheKey, expirationDate, payload );
         memoryCacheStore.store( cacheKey, expirationDate, payload );
-        if ( localDBCacheStore != null )
-        {
-            localDBCacheStore.store( cacheKey, expirationDate, payload );
-        }
         outputTraceInfo();
         outputTraceInfo();
     }
     }
 
 
-    public String get( final CacheKey cacheKey )
+    public <T> T get( final CacheKey cacheKey, final Class<T> classOfT  )
             throws PwmUnrecoverableException
             throws PwmUnrecoverableException
     {
     {
         if ( cacheKey == null )
         if ( cacheKey == null )
@@ -165,20 +152,15 @@ public class CacheService implements PwmService
             return null;
             return null;
         }
         }
 
 
-        String payload = null;
+        Object payload = null;
         if ( memoryCacheStore != null )
         if ( memoryCacheStore != null )
         {
         {
-            payload = memoryCacheStore.read( cacheKey );
-        }
-
-        if ( payload == null && localDBCacheStore != null )
-        {
-            payload = localDBCacheStore.read( cacheKey );
+            payload = memoryCacheStore.read( cacheKey, classOfT );
         }
         }
 
 
         outputTraceInfo();
         outputTraceInfo();
 
 
-        return payload;
+        return (T) payload;
     }
     }
 
 
     private void outputTraceInfo( )
     private void outputTraceInfo( )
@@ -199,12 +181,6 @@ public class CacheService implements PwmService
             traceOutput.append( ", memCache=" );
             traceOutput.append( ", memCache=" );
             traceOutput.append( JsonUtil.serialize( info ) );
             traceOutput.append( JsonUtil.serialize( info ) );
         }
         }
-        if ( localDBCacheStore != null )
-        {
-            final CacheStoreInfo info = localDBCacheStore.getCacheStoreInfo();
-            traceOutput.append( ", localDbCache=" );
-            traceOutput.append( JsonUtil.serialize( info ) );
-        }
         LOGGER.trace( traceOutput );
         LOGGER.trace( traceOutput );
     }
     }
 }
 }

+ 3 - 2
server/src/main/java/password/pwm/svc/cache/CacheStore.java

@@ -24,14 +24,15 @@ package password.pwm.svc.cache;
 
 
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.error.PwmUnrecoverableException;
 
 
+import java.io.Serializable;
 import java.time.Instant;
 import java.time.Instant;
 import java.util.List;
 import java.util.List;
 
 
 public interface CacheStore
 public interface CacheStore
 {
 {
-    void store( CacheKey cacheKey, Instant expirationDate, String data ) throws PwmUnrecoverableException;
+    void store( CacheKey cacheKey, Instant expirationDate, Serializable data ) throws PwmUnrecoverableException;
 
 
-    String read( CacheKey cacheKey ) throws PwmUnrecoverableException;
+    <T> T read( CacheKey cacheKey, Class<T> classOfT ) throws PwmUnrecoverableException;
 
 
     CacheStoreInfo getCacheStoreInfo( );
     CacheStoreInfo getCacheStoreInfo( );
 
 

+ 1 - 12
server/src/main/java/password/pwm/svc/cache/CacheValueWrapper.java → server/src/main/java/password/pwm/svc/cache/CacheValueType.java

@@ -22,17 +22,6 @@
 
 
 package password.pwm.svc.cache;
 package password.pwm.svc.cache;
 
 
-import lombok.AllArgsConstructor;
-import lombok.Getter;
-
-import java.io.Serializable;
-import java.time.Instant;
-
-@Getter
-@AllArgsConstructor
-class CacheValueWrapper implements Serializable
+public enum CacheValueType
 {
 {
-    private final CacheKey cacheKey;
-    private final Instant expirationDate;
-    private final String payload;
 }
 }

+ 0 - 260
server/src/main/java/password/pwm/svc/cache/LocalDBCacheStore.java

@@ -1,260 +0,0 @@
-/*
- * Password Management Servlets (PWM)
- * http://www.pwm-project.org
- *
- * Copyright (c) 2006-2009 Novell, Inc.
- * Copyright (c) 2009-2018 The PWM Project
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-package password.pwm.svc.cache;
-
-import password.pwm.PwmApplication;
-import password.pwm.error.PwmUnrecoverableException;
-import password.pwm.util.java.JavaHelper;
-import password.pwm.util.java.JsonUtil;
-import password.pwm.util.localdb.LocalDB;
-import password.pwm.util.localdb.LocalDBException;
-import password.pwm.util.logging.PwmLogger;
-
-import java.time.Duration;
-import java.time.Instant;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.TimerTask;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.atomic.AtomicInteger;
-
-public class LocalDBCacheStore implements CacheStore
-{
-    private static final PwmLogger LOGGER = PwmLogger.forClass( LocalDBCacheStore.class );
-
-    private static final LocalDB.DB DB = LocalDB.DB.CACHE;
-    private static final int MAX_REMOVALS_PER_CYCLE = 10 * 1000;
-    private static final int TICKS_BETWEEN_PURGE_CYCLES = 1000;
-
-    private final LocalDB localDB;
-    private final ExecutorService timer;
-    private final AtomicInteger ticks = new AtomicInteger( 0 );
-
-    private final CacheStoreInfo cacheStoreInfo = new CacheStoreInfo();
-
-    LocalDBCacheStore( final PwmApplication pwmApplication )
-    {
-        this.localDB = pwmApplication.getLocalDB();
-        try
-        {
-            localDB.truncate( DB );
-        }
-        catch ( LocalDBException e )
-        {
-            LOGGER.error( "error while clearing LocalDB CACHE DB during init: " + e.getMessage() );
-        }
-        timer = JavaHelper.makeSingleThreadExecutorService( pwmApplication, LocalDBCacheStore.class );
-    }
-
-    @Override
-    public void store( final CacheKey cacheKey, final Instant expirationDate, final String data )
-            throws PwmUnrecoverableException
-    {
-        ticks.incrementAndGet();
-        cacheStoreInfo.incrementStoreCount();
-        try
-        {
-            localDB.put( DB, cacheKey.getHash(), JsonUtil.serialize( new CacheValueWrapper( cacheKey, expirationDate, data ) ) );
-        }
-        catch ( LocalDBException e )
-        {
-            LOGGER.error( "error while writing cache: " + e.getMessage() );
-        }
-        if ( ticks.get() > TICKS_BETWEEN_PURGE_CYCLES )
-        {
-            ticks.set( 0 );
-            timer.execute( new PurgerTask() );
-        }
-    }
-
-    @Override
-    public String read( final CacheKey cacheKey )
-            throws PwmUnrecoverableException
-    {
-        cacheStoreInfo.incrementReadCount();
-        final String hashKey = cacheKey.getHash();
-        final String storedValue;
-        try
-        {
-            storedValue = localDB.get( DB, hashKey );
-        }
-        catch ( LocalDBException e )
-        {
-            LOGGER.error( "error while reading cache: " + e.getMessage() );
-            return null;
-        }
-        if ( storedValue != null )
-        {
-            try
-            {
-                final CacheValueWrapper valueWrapper = JsonUtil.deserialize( storedValue, CacheValueWrapper.class );
-                if ( cacheKey.equals( valueWrapper.getCacheKey() ) )
-                {
-                    if ( valueWrapper.getExpirationDate().isAfter( Instant.now() ) )
-                    {
-                        cacheStoreInfo.getHitCount();
-                        return valueWrapper.getPayload();
-                    }
-                }
-            }
-            catch ( Exception e )
-            {
-                LOGGER.error( "error reading from cache: " + e.getMessage() );
-            }
-            try
-            {
-                localDB.remove( DB, hashKey );
-            }
-            catch ( LocalDBException e )
-            {
-                LOGGER.error( "error while purging record from cache: " + e.getMessage() );
-            }
-        }
-        cacheStoreInfo.incrementMissCount();
-        return null;
-    }
-
-    @Override
-    public CacheStoreInfo getCacheStoreInfo( )
-    {
-        return cacheStoreInfo;
-    }
-
-    private boolean purgeExpiredRecords( ) throws LocalDBException
-    {
-        final List<String> removalKeys = new ArrayList<>();
-        final LocalDB.LocalDBIterator<String> localDBIterator = localDB.iterator( DB );
-        int counter = 0;
-        try
-        {
-            while ( localDBIterator.hasNext() && removalKeys.size() < MAX_REMOVALS_PER_CYCLE )
-            {
-                final String key = localDBIterator.next();
-                counter++;
-                boolean keep = false;
-                try
-                {
-                    if ( key != null )
-                    {
-                        final String strValue = localDB.get( DB, key );
-                        if ( strValue != null )
-                        {
-                            final CacheValueWrapper valueWrapper = JsonUtil.deserialize( strValue, CacheValueWrapper.class );
-                            if ( valueWrapper.getExpirationDate().isBefore( Instant.now() ) )
-                            {
-                                keep = true;
-                            }
-                        }
-
-                    }
-                }
-                catch ( Exception e )
-                {
-                    LOGGER.error( "error reading from cache: " + e.getMessage() );
-                }
-                if ( !keep )
-                {
-                    removalKeys.add( key );
-                }
-            }
-        }
-        finally
-        {
-            if ( localDBIterator != null )
-            {
-                localDBIterator.close();
-            }
-        }
-        if ( !removalKeys.isEmpty() )
-        {
-            LOGGER.debug( "purging " + removalKeys.size() + " expired cache records" );
-            localDB.removeAll( DB, removalKeys );
-        }
-        else
-        {
-            LOGGER.trace( "purger examined " + counter + " records and did not discover any expired cache records" );
-        }
-
-        return removalKeys.size() >= MAX_REMOVALS_PER_CYCLE;
-    }
-
-    private class PurgerTask extends TimerTask
-    {
-        @Override
-        public void run( )
-        {
-            try
-            {
-                purgeExpiredRecords();
-            }
-            catch ( LocalDBException e )
-            {
-                LOGGER.error( "error while running purger task: " + e.getMessage(), e );
-            }
-        }
-    }
-
-    @Override
-    public int itemCount( )
-    {
-        try
-        {
-            return localDB.size( DB );
-        }
-        catch ( LocalDBException e )
-        {
-            LOGGER.error( "unexpected error reading size from localDB: " + e.getMessage(), e );
-        }
-        return 0;
-    }
-
-    @Override
-    public List<CacheDebugItem> getCacheDebugItems( )
-    {
-        final List<CacheDebugItem> items = new ArrayList<>();
-        try ( LocalDB.LocalDBIterator<String> iter = localDB.iterator( DB ) )
-        {
-            while ( iter.hasNext() )
-            {
-                final String nextKey = iter.next();
-                final String storedValue = localDB.get( DB, nextKey );
-                if ( storedValue != null )
-                {
-                    final CacheValueWrapper valueWrapper = JsonUtil.deserialize( storedValue, CacheValueWrapper.class );
-                    final String hash = valueWrapper.getCacheKey().getStorageValue();
-                    final int chars = valueWrapper.getPayload().length();
-                    final Instant storeDate = valueWrapper.getExpirationDate();
-                    final String age = Duration.between( storeDate, Instant.now() ).toString();
-                    final CacheDebugItem cacheDebugItem = new CacheDebugItem( hash, age, chars );
-                    items.add( cacheDebugItem );
-                }
-            }
-        }
-        catch ( LocalDBException e )
-        {
-            LOGGER.error( "unexpected error reading debug items: " + e.getMessage(), e );
-        }
-        return Collections.unmodifiableList( items );
-    }
-}

+ 29 - 18
server/src/main/java/password/pwm/svc/cache/MemoryCacheStore.java

@@ -24,20 +24,24 @@ package password.pwm.svc.cache;
 
 
 import com.github.benmanes.caffeine.cache.Cache;
 import com.github.benmanes.caffeine.cache.Cache;
 import com.github.benmanes.caffeine.cache.Caffeine;
 import com.github.benmanes.caffeine.cache.Caffeine;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.error.PwmUnrecoverableException;
+import password.pwm.util.java.JsonUtil;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.logging.PwmLogger;
 
 
+import java.io.Serializable;
 import java.time.Duration;
 import java.time.Duration;
 import java.time.Instant;
 import java.time.Instant;
 import java.util.ArrayList;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Collections;
-import java.util.Iterator;
 import java.util.List;
 import java.util.List;
+import java.util.Map;
 
 
 class MemoryCacheStore implements CacheStore
 class MemoryCacheStore implements CacheStore
 {
 {
     private static final PwmLogger LOGGER = PwmLogger.forClass( MemoryCacheStore.class );
     private static final PwmLogger LOGGER = PwmLogger.forClass( MemoryCacheStore.class );
-    private final Cache<String, CacheValueWrapper> memoryStore;
+    private final Cache<CacheKey, CacheValueWrapper> memoryStore;
     private final CacheStoreInfo cacheStoreInfo = new CacheStoreInfo();
     private final CacheStoreInfo cacheStoreInfo = new CacheStoreInfo();
 
 
     MemoryCacheStore( final int maxItems )
     MemoryCacheStore( final int maxItems )
@@ -48,19 +52,18 @@ class MemoryCacheStore implements CacheStore
     }
     }
 
 
     @Override
     @Override
-    public void store( final CacheKey cacheKey, final Instant expirationDate, final String data )
+    public void store( final CacheKey cacheKey, final Instant expirationDate, final Serializable data )
             throws PwmUnrecoverableException
             throws PwmUnrecoverableException
     {
     {
-        cacheStoreInfo.getStoreCount();
-        memoryStore.put( cacheKey.getHash(), new CacheValueWrapper( cacheKey, expirationDate, data ) );
+        cacheStoreInfo.incrementStoreCount();
+        memoryStore.put( cacheKey, new CacheValueWrapper( cacheKey, expirationDate, data ) );
     }
     }
 
 
     @Override
     @Override
-    public String read( final CacheKey cacheKey )
-            throws PwmUnrecoverableException
+    public <T> T read( final CacheKey cacheKey, final Class<T> classOfT )
     {
     {
-        cacheStoreInfo.getReadCount();
-        final CacheValueWrapper valueWrapper = memoryStore.getIfPresent( cacheKey.getHash() );
+        cacheStoreInfo.incrementReadCount();
+        final CacheValueWrapper valueWrapper = memoryStore.getIfPresent( cacheKey );
         if ( valueWrapper != null )
         if ( valueWrapper != null )
         {
         {
             if ( cacheKey.equals( valueWrapper.getCacheKey() ) )
             if ( cacheKey.equals( valueWrapper.getCacheKey() ) )
@@ -68,11 +71,11 @@ class MemoryCacheStore implements CacheStore
                 if ( valueWrapper.getExpirationDate().isAfter( Instant.now() ) )
                 if ( valueWrapper.getExpirationDate().isAfter( Instant.now() ) )
                 {
                 {
                     cacheStoreInfo.incrementHitCount();
                     cacheStoreInfo.incrementHitCount();
-                    return valueWrapper.getPayload();
+                    return (T) valueWrapper.getPayload();
                 }
                 }
             }
             }
         }
         }
-        memoryStore.invalidate( cacheKey.getHash() );
+        memoryStore.invalidate( cacheKey );
         cacheStoreInfo.incrementMissCount();
         cacheStoreInfo.incrementMissCount();
         return null;
         return null;
     }
     }
@@ -93,17 +96,25 @@ class MemoryCacheStore implements CacheStore
     public List<CacheDebugItem> getCacheDebugItems( )
     public List<CacheDebugItem> getCacheDebugItems( )
     {
     {
         final List<CacheDebugItem> items = new ArrayList<>();
         final List<CacheDebugItem> items = new ArrayList<>();
-        final Iterator<CacheValueWrapper> iter = memoryStore.asMap().values().iterator();
-        while ( iter.hasNext() )
+        for ( Map.Entry<CacheKey, CacheValueWrapper> entry : memoryStore.asMap().entrySet() )
         {
         {
-            final CacheValueWrapper valueWrapper = iter.next();
-            final String hash = valueWrapper.getCacheKey().getStorageValue();
-            final int chars = valueWrapper.getPayload().length();
-            final Instant storeDate = valueWrapper.getExpirationDate();
+            final CacheKey cacheKey = entry.getKey();
+            final CacheValueWrapper cacheValueWrapper = entry.getValue();
+            final Instant storeDate = cacheValueWrapper.getExpirationDate();
             final String age = Duration.between( storeDate, Instant.now() ).toString();
             final String age = Duration.between( storeDate, Instant.now() ).toString();
-            final CacheDebugItem cacheDebugItem = new CacheDebugItem( hash, age, chars );
+            final int chars = JsonUtil.serialize( cacheValueWrapper.getPayload() ).length();
+            final CacheDebugItem cacheDebugItem = new CacheDebugItem( cacheKey, age, chars );
             items.add( cacheDebugItem );
             items.add( cacheDebugItem );
         }
         }
         return Collections.unmodifiableList( items );
         return Collections.unmodifiableList( items );
     }
     }
+
+    @Getter
+    @AllArgsConstructor
+    static class CacheValueWrapper implements Serializable
+    {
+        private final CacheKey cacheKey;
+        private final Instant expirationDate;
+        private final Serializable payload;
+    }
 }
 }

+ 2 - 2
server/src/main/java/password/pwm/util/form/FormUtility.java

@@ -294,12 +294,12 @@ public class FormUtility
         }
         }
 
 
         final CacheService cacheService = pwmApplication.getCacheService();
         final CacheService cacheService = pwmApplication.getCacheService();
-        final CacheKey cacheKey = CacheKey.makeCacheKey(
+        final CacheKey cacheKey = CacheKey.newKey(
                 Validator.class, null, "attr_unique_check_" + filter.toString()
                 Validator.class, null, "attr_unique_check_" + filter.toString()
         );
         );
         if ( allowResultCaching && cacheService != null )
         if ( allowResultCaching && cacheService != null )
         {
         {
-            final String cacheValue = cacheService.get( cacheKey );
+            final String cacheValue = cacheService.get( cacheKey, String.class );
             if ( cacheValue != null )
             if ( cacheValue != null )
             {
             {
                 if ( NEGATIVE_CACHE_HIT.equals( cacheValue ) )
                 if ( NEGATIVE_CACHE_HIT.equals( cacheValue ) )

+ 2 - 2
server/src/main/java/password/pwm/util/operations/PasswordUtility.java

@@ -1020,7 +1020,7 @@ public class PasswordUtility
         {
         {
             final CacheService cacheService = pwmApplication.getCacheService();
             final CacheService cacheService = pwmApplication.getCacheService();
             final CacheKey cacheKey = user != null && userInfo.getUserIdentity() != null
             final CacheKey cacheKey = user != null && userInfo.getUserIdentity() != null
-                    ? CacheKey.makeCacheKey(
+                    ? CacheKey.newKey(
                     PasswordUtility.class,
                     PasswordUtility.class,
                     userInfo.getUserIdentity(),
                     userInfo.getUserIdentity(),
                     user.getEntryDN() + ":" + password.hash() )
                     user.getEntryDN() + ":" + password.hash() )
@@ -1033,7 +1033,7 @@ public class PasswordUtility
             {
             {
                 if ( cacheService != null && cacheKey != null )
                 if ( cacheService != null && cacheKey != null )
                 {
                 {
-                    final String cachedValue = cacheService.get( cacheKey );
+                    final String cachedValue = cacheService.get( cacheKey, String.class );
                     if ( cachedValue != null )
                     if ( cachedValue != null )
                     {
                     {
                         if ( NEGATIVE_CACHE_HIT.equals( cachedValue ) )
                         if ( NEGATIVE_CACHE_HIT.equals( cachedValue ) )