/**
   * The keys returned are a union of the set of keys in the current transaction and those in the
   * backing cache.
   */
  @SuppressWarnings({"unchecked", "rawtypes"})
  public Collection<K> getKeys() {
    Collection<Serializable> keys = null;
    // in-txn layering
    if (AlfrescoTransactionSupport.getTransactionId() != null) {
      keys = new HashSet<Serializable>(23);
      TransactionData txnData = getTransactionData();
      if (!txnData.isClearOn) {
        // the backing cache is not due for a clear
        Collection<K> backingKeys = (Collection<K>) sharedCache.getKeys();
        Collection<Serializable> backingCacheKeys = new HashSet<Serializable>(backingKeys.size());
        for (K backingKey : backingKeys) {
          backingCacheKeys.add(getTenantAwareCacheKey(backingKey));
        }
        keys.addAll(backingCacheKeys);
      }
      // add keys
      keys.addAll(txnData.updatedItemsCache.keySet());
      // remove keys
      keys.removeAll(txnData.removedItemsCache);
    } else {
      // no transaction, so just use the backing cache
      keys = (Collection) sharedCache.getKeys();
    }

    Collection<K> cacheKeys = new HashSet<K>(keys.size());
    String currentCacheRegion = TenantUtil.getCurrentDomain();

    for (Serializable key : keys) {
      if (key instanceof CacheRegionKey) {
        CacheRegionKey cacheRegionKey = (CacheRegionKey) key;
        if (currentCacheRegion.equals(cacheRegionKey.getCacheRegion())) {
          cacheKeys.add((K) cacheRegionKey.getCacheKey());
        }
      } else {
        cacheKeys.add((K) key);
      }
    }
    // done
    return cacheKeys;
  }
  /**
   * Transaction-long setting to force all the share cache to be bypassed for the current
   * transaction.
   *
   * <p>This setting is like having a {@link NullCache null} {@link #setSharedCache(SimpleCache)
   * shared cache}, but only lasts for the transaction.
   *
   * <p>Use this when a read transaction <b>must</b> see consistent and current data i.e. go to the
   * database. While this is active, write operations will also not be committed to the shared
   * cache.
   *
   * @param noSharedCacheRead <tt>true</tt> to avoid reading from the shared cache for the
   *     transaction
   */
  @SuppressWarnings("unchecked")
  public void setDisableSharedCacheReadForTransaction(boolean noSharedCacheRead) {
    TransactionData txnData = getTransactionData();

    // If we are switching on noSharedCacheRead mode, convert all existing reads and updates to
    // avoid 'consistent
    // read' behaviour giving us a potentially out of date node already accessed
    if (noSharedCacheRead && !txnData.noSharedCacheRead) {
      txnData.noSharedCacheRead = noSharedCacheRead;
      String currentCacheRegion = TenantUtil.getCurrentDomain();
      for (Map.Entry<Serializable, CacheBucket<V>> entry :
          new ArrayList<Map.Entry<Serializable, CacheBucket<V>>>(
              txnData.updatedItemsCache.entrySet())) {
        Serializable cacheKey = entry.getKey();
        K key = null;
        if (cacheKey instanceof CacheRegionKey) {
          CacheRegionKey cacheRegionKey = (CacheRegionKey) cacheKey;
          if (currentCacheRegion.equals(cacheRegionKey.getCacheRegion())) {
            key = (K) cacheRegionKey.getCacheKey();
          }
        } else {
          key = (K) cacheKey;
        }

        if (key != null) {
          CacheBucket<V> bucket = entry.getValue();
          // Simply 'forget' reads
          if (bucket instanceof ReadCacheBucket) {
            txnData.updatedItemsCache.remove(cacheKey);
          }
          // Convert updates to removes
          else if (bucket instanceof UpdateCacheBucket) {
            remove(key);
          }
          // Leave new entries alone - they can't have come from the shared cache
        }
      }
    }
  }