Skip to content

Commit

Permalink
Renamed the BulkObjectCache to the correct ObjectCache
Browse files Browse the repository at this point in the history
Removed the defunct SobjectCache
Hid references to the partition specification
Removed 'getKeys' as this should no longer be needed
Removed 'clearAll' from SOQL cache, as this in an inappropriate level (it would clear all Org level cache)
Ensure that all classes have inherited sharing
  • Loading branch information
rob-baillie-ortoo committed Mar 10, 2022
1 parent 377619d commit 8a5dfda
Show file tree
Hide file tree
Showing 12 changed files with 389 additions and 759 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ public inherited sharing class CachedSoqlExecutor //NOPMD: incorrect report of t

ICacheAdaptor cacheWrapper = new OrgCache(); // by default, configure the cache to use the org version

private final static String PARTITION_NAME = 'core';
private final static Integer CACHE_LIFESPAN_SECONDS = 28800; // TODO: soft setting / option

/**
Expand Down Expand Up @@ -118,30 +117,8 @@ public inherited sharing class CachedSoqlExecutor //NOPMD: incorrect report of t
cacheWrapper.remove( generateKey( soql ) );
}

/**
* Clears the cached results for all cached SOQL
*/
public void clearAllCache()
{
String fullSoqlPartitionName = cacheWrapper.createFullyQualifiedPartitionName( PARTITION_NAME );
for ( String thisKey : cacheWrapper.getKeys() )
{
String qualifiedKey = qualifiedKey( thisKey );
if ( cacheWrapper.contains( qualifiedKey ) )
{
cacheWrapper.remove( qualifiedKey );
}
}
}

private String generateKey( String soql )
{
String subkey = EncodingUtil.convertToHex( Crypto.generateDigest( 'SHA1', Blob.valueOf( soql ) ) );
return qualifiedKey( subkey );
}

private String qualifiedKey( String subkey )
{
return cacheWrapper.createFullyQualifiedKey( PARTITION_NAME, subkey );
return EncodingUtil.convertToHex( Crypto.generateDigest( 'SHA1', Blob.valueOf( soql ) ) );
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,10 @@
*/
public interface ICacheAdaptor
{
Boolean hasAccessToCache();
Boolean isACache();
Object get( String key );
void put( String key, Object value, Integer lifespan );
Set<String> getKeys();
Boolean contains( String key );
void remove( String key );
String createFullyQualifiedPartitionName( String partitionName );
String createFullyQualifiedKey( String partitionName, String key );
Boolean hasAccessToCache();
Boolean isACache();
Object get( String key );
void put( String key, Object value, Integer lifespan );
Boolean contains( String key );
void remove( String key );
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,48 +5,33 @@
*
* All users are assumed to be allowed to use the cache, but it describes itself as 'not a cache' and effectively does nothing.
*/
public class NullCache implements ICacheAdaptor
public inherited sharing class NullCache implements ICacheAdaptor
{
public Boolean hasAccessToCache()
{
return true;
}

public Boolean isACache()
{
return false;
}

public Object get( String key )
{
return null;
}

public void put( String key, Object value, Integer lifespan ) // NOPMD: Intentionally left empty, as this should do nothing in a NullCache
{
}

public Set<String> getKeys()
{
return new Set<String>();
}

public Boolean contains( String key )
{
return false;
}

public void remove( String key ) // NOPMD: Intentionally left empty, as this should do nothing in a NullCache
{
}

public String createFullyQualifiedPartitionName( String partitionName )
{
return partitionName;
}

public String createFullyQualifiedKey( String partitionName, String subKey )
{
return partitionName + '.' + subkey;
}
public Boolean hasAccessToCache()
{
return true;
}

public Boolean isACache()
{
return false;
}

public Object get( String key )
{
return null;
}

public void put( String key, Object value, Integer lifespan ) // NOPMD: Intentionally left empty, as this should do nothing in a NullCache
{
}

public Boolean contains( String key )
{
return false;
}

public void remove( String key ) // NOPMD: Intentionally left empty, as this should do nothing in a NullCache
{
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,14 @@
* Note: The lifespan of a given object is reset whenever any object in that object's list is written.
* That is, either the whole of the list is aged out, or none of it is.
*/
public with sharing class BulkObjectCache
public with sharing class ObjectCache
{
// TODO: do we need to protect against 100kb limit
// TODO: could implement a longest time since accessed, first out
// TODO: could implement a random age out
// TODO: could just ignore and invalidate the cache - check with the rest of the seniors
// TODO: Docs on keys

// TODO: hide createFullyQualifiedPartitionName and createFullyQualifiedKey

public class InvalidIdentifierException extends Exceptions.DeveloperException {}

public enum CacheScope { ORG, SESSION }
Expand Down Expand Up @@ -80,7 +78,6 @@ public with sharing class BulkObjectCache

ICacheAdaptor cacheWrapper = new OrgCache(); // by default, configure the cache to use the org version

private final static String PARTITION_NAME = 'core';
private final static Integer CACHE_LIFESPAN_SECONDS = 28800; // TODO: soft setting / option

/**
Expand All @@ -89,7 +86,7 @@ public with sharing class BulkObjectCache
* @param CacheScope The scope of this cache instance.
* @return CachedSoqlExecutor Itself, allowing for a fluent interface
*/
public BulkObjectCache setScope( CacheScope scope )
public ObjectCache setScope( CacheScope scope )
{
Contract.requires( scope != null, 'setScope called with a null scope' );

Expand Down Expand Up @@ -122,7 +119,7 @@ public with sharing class BulkObjectCache

CacheRetrieval values = new CacheRetrieval();

Map<String,Object> cachedObjects = (Map<String,Object>)cacheWrapper.get( createFullyQualifiedKey( key ) );
Map<String,Object> cachedObjects = (Map<String,Object>)cacheWrapper.get( key );

if ( cachedObjects == null )
{
Expand Down Expand Up @@ -194,9 +191,9 @@ public with sharing class BulkObjectCache
*
* @param String The base key to put the objects into
* @param List<Sobject> The SObjects to store
* @return BulkObjectCache Itself, allowing for a fluent interface
* @return ObjectCache Itself, allowing for a fluent interface
*/
public BulkObjectCache put( String key, List<Sobject> sobjects )
public ObjectCache put( String key, List<Sobject> sobjects )
{
return put( key, 'Id', sobjects );
}
Expand All @@ -208,9 +205,9 @@ public with sharing class BulkObjectCache
* @param String The base key to put the objects into
* @param String The field to get the individual SObject identifiers from (field value must be a non-null String)
* @param List<Sobject> The SObjects to store
* @return BulkObjectCache Itself, allowing for a fluent interface
* @return ObjectCache Itself, allowing for a fluent interface
*/
public BulkObjectCache put( String key, String idField, List<Sobject> sobjects )
public ObjectCache put( String key, String idField, List<Sobject> sobjects )
{
Contract.requires( idField != null, 'put called with a null idField' );
return put( key, new SobjectIdGetter( idField ), sobjects );
Expand All @@ -223,16 +220,15 @@ public with sharing class BulkObjectCache
* @param String The base key to put the objects into
* @param IdGetter The mechanism for getting the Id from each of the given objects
* @param List<Object> The Objects to store
* @return BulkObjectCache Itself, allowing for a fluent interface
* @return ObjectCache Itself, allowing for a fluent interface
*/
public BulkObjectCache put( String key, IdGetter idGetter, List<Object> objects )
public ObjectCache put( String key, IdGetter idGetter, List<Object> objects )
{
Contract.requires( key != null, 'put called with a null key' );
Contract.requires( idGetter != null, 'put called with a null idGetter' );
Contract.requires( objects != null, 'put called with a null objects' );

String fullyQualifiedKey = createFullyQualifiedKey( key );
Map<String,Object> cachedObjects = (Map<String,Object>)cacheWrapper.get( fullyQualifiedKey );
Map<String,Object> cachedObjects = (Map<String,Object>)cacheWrapper.get( key );

if ( cachedObjects == null )
{
Expand All @@ -247,7 +243,7 @@ public with sharing class BulkObjectCache
Contract.assert( thisId != null, 'put called with an object that has a null Id: ' + thisObject );
cachedObjects.put( thisId, thisObject );
}
cacheWrapper.put( fullyQualifiedKey, cachedObjects, CACHE_LIFESPAN_SECONDS );
cacheWrapper.put( key, cachedObjects, CACHE_LIFESPAN_SECONDS );

return this;
}
Expand All @@ -256,12 +252,12 @@ public with sharing class BulkObjectCache
* Remove all the cached objects from the given base key.
*
* @param String The base key to remove the objects from.
* @return BulkObjectCache Itself, allowing for a fluent interface
* @return ObjectCache Itself, allowing for a fluent interface
*/
public BulkObjectCache remove( String key )
public ObjectCache remove( String key )
{
Contract.requires( key != null, 'remove called with a null key' );
cacheWrapper.remove( createFullyQualifiedKey( key ) );
cacheWrapper.remove( key );
return this;
}

Expand All @@ -270,9 +266,9 @@ public with sharing class BulkObjectCache
*
* @param String The base key to remove the SObjects from.
* @param Set<Id> The cache identifiers of the SObjects to remove.
* @return BulkObjectCache Itself, allowing for a fluent interface
* @return ObjectCache Itself, allowing for a fluent interface
*/
public BulkObjectCache remove( String key, Set<Id> ids )
public ObjectCache remove( String key, Set<Id> ids )
{
Contract.requires( ids != null, 'remove called with a null ids' );
return remove( key, SetUtils.convertToSetOfStrings( ids ) );
Expand All @@ -283,29 +279,24 @@ public with sharing class BulkObjectCache
*
* @param String The base key to remove the objects from.
* @param Set<Id> The cache identifiers of the objects to remove.
* @return BulkObjectCache Itself, allowing for a fluent interface
* @return ObjectCache Itself, allowing for a fluent interface
*/
public BulkObjectCache remove( String key, Set<String> ids )
public ObjectCache remove( String key, Set<String> ids )
{
Contract.requires( key != null, 'remove called with a null key' );
Contract.requires( ids != null, 'remove called with a null ids' );

Map<String,Object> cachedObjects = (Map<String,Object>)cacheWrapper.get( createFullyQualifiedKey( key ) );
Map<String,Object> cachedObjects = (Map<String,Object>)cacheWrapper.get( key );
for ( String thisId : ids )
{
Contract.assert( thisId != null, 'remove called with a null entry in ids' );

cachedObjects.remove( thisId );
}
cacheWrapper.put( createFullyQualifiedKey( key ), cachedObjects, CACHE_LIFESPAN_SECONDS );
cacheWrapper.put( key, cachedObjects, CACHE_LIFESPAN_SECONDS );
return this;
}

private String createFullyQualifiedKey( String key )
{
return cacheWrapper.createFullyQualifiedKey( PARTITION_NAME, key );
}

private class SobjectIdGetter implements IdGetter
{
String idField;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,14 @@
*
* Attempting to access the cache without this permission will result in an OrgCache.AccessViolationException
*/
public class OrgCache implements ICacheAdaptor
public inherited sharing class OrgCache implements ICacheAdaptor
{
// TODO: clear all
public class AccessViolationException extends Exceptions.DeveloperException {} // this looks like a config exception, but actually the system should be built
// in such a way that it's never possible to get this exception

private final static String PARTITION_NAME = 'core';

private Boolean hasAccessToCache
{
get
Expand Down Expand Up @@ -65,7 +68,7 @@ public class OrgCache implements ICacheAdaptor
.addContext( 'key', key );
}

return Cache.Org.get( key );
return Cache.Org.get( createFullyQualifiedKey( key ) );
}

/**
Expand Down Expand Up @@ -93,26 +96,7 @@ public class OrgCache implements ICacheAdaptor
.addContext( 'value', value );
}

Cache.Org.put( key, value, lifespan, Cache.Visibility.NAMESPACE, true ); // immutable outside of namespace
}

/**
* Retrieve a set of the current keys of objects stored in this cache.
*
* If the user does not have access to the cache, will throw an AccessViolationException.
*
* @return Set<String> The keys that currently exist in the cache
*/
public Set<String> getKeys()
{
if ( ! hasAccessToCache )
{
throw new AccessViolationException( Label.ortoo_core_org_cache_access_violation )
.setErrorCode( FrameworkErrorCodes.CACHE_ACCESS_VIOLATION )
.addContext( 'method', 'getKeys' );
}

return Cache.Org.getKeys();
Cache.Org.put( createFullyQualifiedKey( key ), value, lifespan, Cache.Visibility.NAMESPACE, true ); // immutable outside of namespace
}

/**
Expand All @@ -132,7 +116,7 @@ public class OrgCache implements ICacheAdaptor
.addContext( 'method', 'contains' )
.addContext( 'key', key );
}
return Cache.Org.contains( key );
return Cache.Org.contains( createFullyQualifiedKey( key ) );
}

/**
Expand All @@ -151,16 +135,16 @@ public class OrgCache implements ICacheAdaptor
.addContext( 'method', 'remove' )
.addContext( 'key', key );
}
Cache.Org.remove( key );
Cache.Org.remove( createFullyQualifiedKey( key ) );
}

public String createFullyQualifiedPartitionName( String partitionName )
private String createFullyQualifiedPartitionName()
{
return Cache.OrgPartition.createFullyQualifiedPartition( PackageUtils.NAMESPACE_PREFIX, partitionName );
return Cache.OrgPartition.createFullyQualifiedPartition( PackageUtils.NAMESPACE_PREFIX, PARTITION_NAME );
}

public String createFullyQualifiedKey( String partitionName, String subKey )
private String createFullyQualifiedKey( String key )
{
return Cache.OrgPartition.createFullyQualifiedKey( PackageUtils.NAMESPACE_PREFIX, partitionName, subkey );
return Cache.OrgPartition.createFullyQualifiedKey( PackageUtils.NAMESPACE_PREFIX, PARTITION_NAME, key );
}
}
Loading

0 comments on commit 8a5dfda

Please sign in to comment.