/**
   * Goes direct to the shared cache in the absence of a transaction.
   *
   * <p>Where a transaction is present, a cache of removed items is lazily added to the thread and
   * the <tt>Object</tt> put onto that.
   */
  public void remove(K keyIn) {
    final Serializable key = getTenantAwareCacheKey(keyIn);

    // are we in a transaction?
    if (AlfrescoTransactionSupport.getTransactionId() == null) // not in transaction
    {
      // no transaction
      sharedCache.remove(key);
      // done
      if (isDebugEnabled) {
        logger.debug(
            "No transaction - removing item from shared cache: \n"
                + "   cache: "
                + this
                + "\n"
                + "   key: "
                + key);
      }
    } else // transaction present
    {
      TransactionData txnData = getTransactionData();
      // Ensure that the cache isn't being modified
      if (txnData.isClosed) {
        if (isDebugEnabled) {
          logger.debug("In post-commit remove: \n" + "   cache: " + this + "\n" + "   key: " + key);
        }
      } else if (isValueLocked(txnData, key)) {
        // The key has been locked
        if (isDebugEnabled) {
          logger.debug(
              "Ignoring remove after detecting locked key: \n"
                  + "   cache: "
                  + this
                  + "\n"
                  + "   key: "
                  + key);
        }
      } else {
        // is the shared cache going to be cleared?
        if (txnData.isClearOn) {
          // don't store removals if we're just going to clear it all out later
        } else {
          // are we in an overflow condition?
          if (txnData.removedItemsCache.size() >= maxCacheSize) {
            // overflow about to occur or has occured - we can only guarantee non-stale
            // data by clearing the shared cache after the transaction.  Also, the
            // shared cache needs to be ignored for the rest of the transaction.
            txnData.isClearOn = true;
            if (!txnData.haveIssuedFullWarning) {
              if (logger.isInfoEnabled()) {
                Exception e = new Exception("Stack: ");
                logger.info(
                    "Transactional removal cache '" + name + "' is full (" + maxCacheSize + ").",
                    e);
              } else if (logger.isWarnEnabled()) {
                logger.warn(
                    "Transactional removal cache '" + name + "' is full (" + maxCacheSize + ").");
              }
              txnData.haveIssuedFullWarning = true;
            }
          } else {
            // Create a bucket to remove the value from the shared cache
            txnData.removedItemsCache.add(key);
          }
        }
        // remove the item from the udpated cache, if present
        txnData.updatedItemsCache.remove(key);
        // done
        if (isDebugEnabled) {
          logger.debug(
              "In transaction - adding item direct to transactional removed cache: \n"
                  + "   cache: "
                  + this
                  + "\n"
                  + "   key: "
                  + key);
        }
      }
    }
  }
  /**
   * Goes direct to the shared cache in the absence of a transaction.
   *
   * <p>Where a transaction is present, a cache of updated items is lazily added to the thread and
   * the <tt>Object</tt> put onto that.
   */
  public void put(K keyIn, V value) {
    final Serializable key = getTenantAwareCacheKey(keyIn);

    // are we in a transaction?
    if (AlfrescoTransactionSupport.getTransactionId() == null) // not in transaction
    {
      // no transaction
      TransactionalCache.putSharedCacheValue(sharedCache, key, value, null);
      // done
      if (isDebugEnabled) {
        logger.debug(
            "No transaction - adding item direct to shared cache: \n"
                + "   cache: "
                + this
                + "\n"
                + "   key: "
                + key
                + "\n"
                + "   value: "
                + value);
      }
    } else // transaction present
    {
      TransactionData txnData = getTransactionData();
      // Ensure that the cache isn't being modified
      if (txnData.isClosed) {
        if (isDebugEnabled) {
          logger.debug(
              "In post-commit add: \n"
                  + "   cache: "
                  + this
                  + "\n"
                  + "   key: "
                  + key
                  + "\n"
                  + "   value: "
                  + value);
        }
      } else if (isValueLocked(txnData, key)) {
        // The key has been locked
        if (isDebugEnabled) {
          logger.debug(
              "Ignoring put after detecting locked key: \n"
                  + "   cache: "
                  + this
                  + "\n"
                  + "   key: "
                  + key
                  + "\n"
                  + "   value: "
                  + value);
        }
      } else {
        // we have an active transaction - add the item into the updated cache for this transaction
        // are we in an overflow condition?
        if (txnData.updatedItemsCache.hasHitSize()) {
          // overflow about to occur or has occured - we can only guarantee non-stale
          // data by clearing the shared cache after the transaction.  Also, the
          // shared cache needs to be ignored for the rest of the transaction.
          txnData.isClearOn = true;
          if (!txnData.haveIssuedFullWarning) {
            if (logger.isInfoEnabled()) {
              Exception e = new Exception("Stack: ");
              logger.info(
                  "Transactional update cache '" + name + "' is full (" + maxCacheSize + ").", e);
            } else if (logger.isWarnEnabled()) {
              logger.warn(
                  "Transactional update cache '" + name + "' is full (" + maxCacheSize + ").");
            }
            txnData.haveIssuedFullWarning = true;
          }
        }
        ValueHolder<V> existingValueHolder =
            txnData.noSharedCacheRead ? null : sharedCache.get(key);
        CacheBucket<V> bucket = null;
        if (existingValueHolder == null) {
          // ALF-5134: Performance of Alfresco cluster less than performance of single node
          // The 'null' marker that used to be inserted also triggered an update in the afterCommit
          // phase; the update triggered cache invalidation in the cluster.  Now, the null cannot
          // be verified to be the same null - there is no null equivalence
          //
          // The value didn't exist before
          bucket = new NewCacheBucket<V>(value);
        } else {
          // Record the existing value as is
          bucket = new UpdateCacheBucket<V>(existingValueHolder, value);
        }
        txnData.updatedItemsCache.put(key, bucket);
        // remove the item from the removed cache, if present
        txnData.removedItemsCache.remove(key);
        // done
        if (isDebugEnabled) {
          logger.debug(
              "In transaction - adding item direct to transactional update cache: \n"
                  + "   cache: "
                  + this
                  + "\n"
                  + "   key: "
                  + key
                  + "\n"
                  + "   value: "
                  + value);
        }
      }
    }
  }