All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.terracotta.modules.ehcache.ToolkitInstanceFactoryImpl Maven / Gradle / Ivy

Go to download

Ehcache is an open source, standards-based cache used to boost performance, offload the database and simplify scalability. Ehcache is robust, proven and full-featured and this has made it the most widely-used Java-based cache.

There is a newer version: 2.10.9.2
Show newest version
/*
 * All content copyright Terracotta, Inc., unless otherwise indicated. All rights reserved.
 */
package org.terracotta.modules.ehcache;

import net.sf.ehcache.Ehcache;
import net.sf.ehcache.config.CacheConfiguration;
import net.sf.ehcache.config.ConfigurationFactory;
import net.sf.ehcache.config.PinningConfiguration;
import net.sf.ehcache.config.TerracottaClientConfiguration;
import net.sf.ehcache.config.TerracottaConfiguration;
import net.sf.ehcache.config.TerracottaConfiguration.Consistency;
import net.sf.ehcache.config.generator.ConfigurationUtil;
import net.sf.ehcache.search.attribute.AttributeExtractor;
import net.sf.ehcache.transaction.Decision;
import net.sf.ehcache.transaction.TransactionID;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.terracotta.modules.ehcache.async.AsyncConfig;
import org.terracotta.modules.ehcache.collections.SerializationHelper;
import org.terracotta.modules.ehcache.collections.SerializedToolkitCache;
import org.terracotta.modules.ehcache.event.CacheDisposalNotification;
import org.terracotta.modules.ehcache.event.CacheEventNotificationMsg;
import org.terracotta.modules.ehcache.store.CacheConfigChangeNotificationMsg;
import org.terracotta.modules.ehcache.store.TerracottaClusteredInstanceFactory;
import org.terracotta.modules.ehcache.store.ToolkitNonStopConfiguration;
import org.terracotta.modules.ehcache.store.nonstop.ToolkitNonstopDisableConfig;
import org.terracotta.modules.ehcache.transaction.ClusteredSoftLockIDKey;
import org.terracotta.modules.ehcache.transaction.SerializedReadCommittedClusteredSoftLock;
import org.terracotta.modules.ehcache.wan.WANUtil;
import org.terracotta.modules.ehcache.wan.Watchable;
import org.terracotta.modules.ehcache.wan.Watchdog;
import org.terracotta.toolkit.Toolkit;
import org.terracotta.toolkit.ToolkitFeatureType;
import org.terracotta.toolkit.ToolkitObjectType;
import org.terracotta.toolkit.builder.ToolkitCacheConfigBuilder;
import org.terracotta.toolkit.builder.ToolkitStoreConfigBuilder;
import org.terracotta.toolkit.cache.ToolkitCache;
import org.terracotta.toolkit.collections.ToolkitMap;
import org.terracotta.toolkit.concurrent.locks.ToolkitLock;
import org.terracotta.toolkit.concurrent.locks.ToolkitReadWriteLock;
import org.terracotta.toolkit.config.Configuration;
import org.terracotta.toolkit.events.ToolkitNotifier;
import org.terracotta.toolkit.internal.ToolkitInternal;
import org.terracotta.toolkit.internal.ToolkitLogger;
import org.terracotta.toolkit.internal.cache.BufferingToolkitCache;
import org.terracotta.toolkit.internal.cache.ToolkitCacheInternal;
import org.terracotta.toolkit.internal.collections.ToolkitListInternal;
import org.terracotta.toolkit.internal.store.ConfigFieldsInternal;
import org.terracotta.toolkit.nonstop.NonStopConfigurationRegistry;
import org.terracotta.toolkit.store.ToolkitConfigFields;

import com.terracotta.entity.ClusteredEntityManager;
import com.terracotta.entity.ehcache.ClusteredCache;
import com.terracotta.entity.ehcache.ClusteredCacheConfiguration;
import com.terracotta.entity.ehcache.ClusteredCacheManager;
import com.terracotta.entity.ehcache.ClusteredCacheManagerConfiguration;
import com.terracotta.entity.ehcache.EhcacheEntitiesNaming;
import com.terracotta.entity.ehcache.ToolkitBackedClusteredCache;
import com.terracotta.entity.ehcache.ToolkitBackedClusteredCacheManager;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Serializable;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import static java.lang.String.format;

public class ToolkitInstanceFactoryImpl implements ToolkitInstanceFactory {

  public static final Logger           LOGGER                                   = LoggerFactory
                                                                                      .getLogger(ToolkitInstanceFactoryImpl.class);
  private static final String CONFIG_LOGGER_NAME = "com.terracotta.ehcache.config";

  public static final String  DELIMITER                                = "|";
  private static final String EVENT_NOTIFIER_SUFFIX                    = "event-notifier";
  private static final String DISPOSAL_NOTIFIER_SUFFIX                 = "disposal-notifier";
  private static final String EHCACHE_NAME_PREFIX                      = "__tc_clustered-ehcache";
  private static final String CONFIG_NOTIFIER_SUFFIX                   = "config-notifier";
  private static final String EHCACHE_TXNS_DECISION_STATE_MAP_NAME     = EHCACHE_NAME_PREFIX + DELIMITER
                                                                                    + "txnsDecision";
  private static final String ALL_SOFT_LOCKS_MAP_SUFFIX                = "softLocks";
  private static final String NEW_SOFT_LOCKS_LIST_SUFFIX               = "newSoftLocks";

  private static final String LOCK_TAG                                 = "::LOCK";

  static final String         CLUSTERED_STORE_CONFIG_MAP               = EHCACHE_NAME_PREFIX + DELIMITER + "configMap";

  private static final String EHCACHE_TXNS_SOFTLOCK_WRITE_LOCK_NAME    = EHCACHE_NAME_PREFIX + DELIMITER
                                                                                    + "softWriteLock";

  private static final String EHCACHE_TXNS_SOFTLOCK_FREEZE_LOCK_NAME   = EHCACHE_NAME_PREFIX + DELIMITER
                                                                                    + "softFreezeLock";
  private static final String EHCACHE_TXNS_SOFTLOCK_NOTIFIER_LOCK_NAME = EHCACHE_NAME_PREFIX + DELIMITER
                                                                                    + "softNotifierLock";
  public static final int RETRY_MARK_IN_USE_AFTER_REJOIN = 5;

  protected final Toolkit                 toolkit;
  private final WANUtil                   wanUtil;
  private final ClusteredEntityManager    clusteredEntityManager;
  private volatile ClusteredCacheManager  clusteredCacheManagerEntity;
  private final EntityNamesHolder         entityNames;
  private final Watchdog                  wanWatchdog;

  public ToolkitInstanceFactoryImpl(final TerracottaClientConfiguration terracottaClientConfiguration,
                                    final String productId, ClassLoader loader) {
    this.toolkit = createTerracottaToolkit(terracottaClientConfiguration, productId, loader);
    updateDefaultNonStopConfig(toolkit);
    this.clusteredEntityManager = new ClusteredEntityManager(toolkit);
    this.entityNames = new EntityNamesHolder();
    this.wanUtil = new WANUtil(this);
    this.wanWatchdog = Watchdog.create();
  }

  public ToolkitInstanceFactoryImpl(final TerracottaClientConfiguration terracottaClientConfiguration,
                                    ClassLoader loader) {
    this(terracottaClientConfiguration, null, loader);
  }

  // Constructor to enable unit testing
  ToolkitInstanceFactoryImpl(final Toolkit toolkit, final ClusteredEntityManager clusteredEntityManager) {
    this.toolkit = toolkit;
    this.clusteredEntityManager = clusteredEntityManager;
    this.entityNames = new EntityNamesHolder();
    this.wanUtil = new WANUtil(this);
    this.wanWatchdog = Watchdog.create();
  }

  ToolkitInstanceFactoryImpl(final Toolkit toolkit, final ClusteredEntityManager clusteredEntityManager,
                             final WANUtil util, final Watchdog wanWatchdog) {
    this.toolkit = toolkit;
    this.clusteredEntityManager = clusteredEntityManager;
    this.entityNames = new EntityNamesHolder();
    this.wanUtil = util;
    this.wanWatchdog = wanWatchdog;
  }

  private void updateDefaultNonStopConfig(Toolkit toolkitParam) {
    ToolkitNonstopDisableConfig disableNonStop = new ToolkitNonstopDisableConfig();
    NonStopConfigurationRegistry nonStopConfigurationRegistry = toolkitParam.getFeature(ToolkitFeatureType.NONSTOP)
        .getNonStopConfigurationRegistry();
    for (ToolkitObjectType t : ToolkitObjectType.values()) {
      try {
        nonStopConfigurationRegistry.registerForType(disableNonStop, t);
      } catch (UnsupportedOperationException e) {
        // expected for Barrier and BlockingQueue.
        if (!(t == ToolkitObjectType.BARRIER || t == ToolkitObjectType.BLOCKING_QUEUE)) { throw e; }
      }
    }
  }

  private static Toolkit createTerracottaToolkit(TerracottaClientConfiguration terracottaClientConfiguration,
                                                 String productId, ClassLoader loader) {
    TerracottaToolkitBuilder terracottaClientBuilder = new TerracottaToolkitBuilder();
    EhcacheTcConfig ehcacheTcConfig = EhcacheTcConfig.create(terracottaClientConfiguration);
    switch (ehcacheTcConfig.type) {
      case URL:
        terracottaClientBuilder.setTCConfigUrl(ehcacheTcConfig.tcConfigUrlOrSnippet);
        break;
      case EMBEDDED_TC_CONFIG:
      case FILE:
        terracottaClientBuilder.setTCConfigSnippet(ehcacheTcConfig.tcConfigUrlOrSnippet);
        break;
    }
    terracottaClientBuilder.addTunnelledMBeanDomain("net.sf.ehcache");
    terracottaClientBuilder.addTunnelledMBeanDomain("net.sf.ehcache.hibernate");
    terracottaClientBuilder.addTunnelledMBeanDomain("org.terracotta.wan");
    terracottaClientBuilder.setRejoinEnabled(terracottaClientConfiguration.isRejoin());
    terracottaClientBuilder.setProductId(productId);
    terracottaClientBuilder.setClassLoader(loader);
    return terracottaClientBuilder.buildToolkit();
  }

  @Override
  public void waitForOrchestrator(String cacheManagerName) {
    wanUtil.waitForOrchestrator(cacheManagerName);
  }

  @Override
  public void markCacheWanDisabled(String cacheManagerName, String cacheName) {
    wanUtil.markCacheWanDisabled(cacheManagerName, cacheName);
  }

  @Override
  public Toolkit getToolkit() {
    return toolkit;
  }

  @Override
  public ToolkitCacheInternal getOrCreateToolkitCache(final Ehcache cache) {
    final String cacheManagerName = getCacheManagerName(cache);
    final String cacheName = cache.getName();
    
    ToolkitCacheInternal toolkitCache = getOrCreateRegularToolkitCache(
        cacheManagerName, cacheName, cache.getCacheConfiguration());
    if(wanUtil.isWanEnabledCache(cacheManagerName, cacheName)) {
      final boolean replicaCache = wanUtil.isCacheReplica(cacheManagerName, cacheName);
      final boolean bidirectional = wanUtil.isCacheBidirectional(cacheManagerName, cacheName);
      toolkitCache = createWanAwareToolkitCache(cacheManagerName, cacheName, toolkitCache,
                                                cache.getCacheConfiguration(), !replicaCache, bidirectional);

      if (replicaCache) {
        LOGGER.info("Pinning the Cache '{}' belonging to Cache Manager '{}' " +
                    "and setting its TTI and TTL values to zero as it is a WAN Replica Cache. " +
                    "This cache's capacity will be controlled by its Master cache.",
            cacheName, cacheManagerName);
        PinningConfiguration pinningConfiguration = new PinningConfiguration();
        pinningConfiguration.setStore(PinningConfiguration.Store.INCACHE.toString());
        cache.getCacheConfiguration().addPinning(pinningConfiguration);
        cache.getCacheConfiguration().setMaxEntriesInCache(0);
        cache.getCacheConfiguration().setTimeToLiveSeconds(0);
        cache.getCacheConfiguration().setTimeToIdleSeconds(0);
      }

      cache.getCacheConfiguration().freezeConfiguration();

      wanWatchdog.watch((Watchable) toolkitCache);
    }

    return toolkitCache;
  }

  @Override
  public WanAwareToolkitCache getOrCreateWanAwareToolkitCache(final String cacheManagerName,
                                                                                    final String cacheName,
                                                                                    final CacheConfiguration ehcacheConfig,
                                                                                    final boolean masterCache,
                                                                                    final boolean bidirectional) {
    final ToolkitCacheInternal toolkitCache =
        getOrCreateRegularToolkitCache(cacheManagerName, cacheName, ehcacheConfig);
    return createWanAwareToolkitCache(cacheManagerName, cacheName, toolkitCache, ehcacheConfig, masterCache, bidirectional);
  }

  private WanAwareToolkitCache createWanAwareToolkitCache(final String cacheManagerName,
                                                                                final String cacheName,
                                                                                final ToolkitCacheInternal toolkitCache,
                                                                                final CacheConfiguration cacheConfiguration,
                                                                                final boolean masterCache,
                                                                                final boolean bidirectional) {
    final String fullyQualifiedCacheName = EhcacheEntitiesNaming.getToolkitCacheNameFor(cacheManagerName, cacheName);
    final ToolkitMap configMap = getOrCreateConfigMap(fullyQualifiedCacheName);
    return new WanAwareToolkitCache((BufferingToolkitCache)toolkitCache, configMap,
                                                          toolkit.getFeature(ToolkitFeatureType.NONSTOP),
                                                          toolkit.getLock(toolkitCache.getName() + LOCK_TAG),
                                                          cacheConfiguration, masterCache, bidirectional);
  }

  private ToolkitCacheInternal getOrCreateRegularToolkitCache(final String cacheManagerName,
                                                                                    final String cacheName,
                                                                                    final CacheConfiguration ehcacheConfig) {
    final Configuration toolkitCacheConfig = createClusteredCacheConfig(ehcacheConfig, cacheManagerName);
    final String fullyQualifiedCacheName = EhcacheEntitiesNaming.getToolkitCacheNameFor(cacheManagerName, cacheName);
    addNonStopConfigForCache(ehcacheConfig, fullyQualifiedCacheName);
    ToolkitCacheInternal toolkitCache = getOrCreateToolkitCache(fullyQualifiedCacheName, toolkitCacheConfig);
    addCacheEntityInfo(cacheName, ehcacheConfig, fullyQualifiedCacheName);
    return toolkitCache;
  }

  private ToolkitCacheInternal getOrCreateToolkitCache(final String fullyQualifiedCacheName,
                                                                             final Configuration toolkitCacheConfig) {
    return (ToolkitCacheInternal) toolkit.getCache(fullyQualifiedCacheName, toolkitCacheConfig,
                                                                         Serializable.class);
  }

  @Override
  public ToolkitNotifier getOrCreateConfigChangeNotifier(Ehcache cache) {
    return getOrCreateConfigChangeNotifier(cache.getCacheManager().getName(), cache.getName());
  }

  private ToolkitNotifier getOrCreateConfigChangeNotifier(String cacheManagerName, String cacheName) {
    String notifierName = EhcacheEntitiesNaming.getToolkitCacheNameFor(cacheManagerName, cacheName) + DELIMITER
                          + CONFIG_NOTIFIER_SUFFIX;
    ToolkitNotifier notifier = toolkit
        .getNotifier(notifierName, CacheConfigChangeNotificationMsg.class);
    addCacheMetaInfo(cacheName, ToolkitObjectType.NOTIFIER, notifierName);
    return notifier;
  }

  @Override
  public ToolkitNotifier getOrCreateCacheEventNotifier(Ehcache cache) {
    return getOrCreateCacheEventNotifier(cache.getCacheManager().getName(), cache.getName());
  }

  @Override
  public ToolkitNotifier getOrCreateCacheDisposalNotifier(Ehcache cache) {
    return toolkit.getNotifier(EhcacheEntitiesNaming.getToolkitCacheNameFor(cache.getCacheManager().getName(), cache.getName())
                               + DELIMITER + DISPOSAL_NOTIFIER_SUFFIX, CacheDisposalNotification.class);
  }

  private ToolkitNotifier getOrCreateCacheEventNotifier(String cacheManagerName, String cacheName) {
    String notifierName = EhcacheEntitiesNaming.getToolkitCacheNameFor(cacheManagerName, cacheName) + DELIMITER
                          + EVENT_NOTIFIER_SUFFIX;
    ToolkitNotifier notifier = toolkit.getNotifier(notifierName,
                                                                              CacheEventNotificationMsg.class);
    addCacheMetaInfo(cacheName, ToolkitObjectType.NOTIFIER, notifierName);
    return notifier;
  }

  private static Configuration createClusteredCacheConfig(final CacheConfiguration ehcacheConfig,
                                                          final String cacheManagerName) {
    ToolkitCacheConfigBuilder builder = new ToolkitCacheConfigBuilder();
    final TerracottaConfiguration terracottaConfiguration = ehcacheConfig.getTerracottaConfiguration();
    builder.maxTTISeconds((int) ehcacheConfig.getTimeToIdleSeconds());
    builder.maxTTLSeconds((int) ehcacheConfig.getTimeToLiveSeconds());
    builder.localCacheEnabled(terracottaConfiguration.isLocalCacheEnabled());

    // Fix for Dev-9223. Dont set anything incase of Default value. Assuming tookit and ehcache defaults are aligned.
    if (ehcacheConfig.getMaxEntriesInCache() != CacheConfiguration.DEFAULT_MAX_ENTRIES_IN_CACHE) {
      if (ehcacheConfig.getMaxEntriesInCache() > Integer.MAX_VALUE) {
        throw new IllegalArgumentException("Values greater than Integer.MAX_VALUE are not currently supported.");
      } else {
        builder.maxTotalCount((int) ehcacheConfig.getMaxEntriesInCache());
      }
    }

    if (terracottaConfiguration.isSynchronousWrites()) {
      builder.consistency(org.terracotta.toolkit.store.ToolkitConfigFields.Consistency.SYNCHRONOUS_STRONG);
    } else if (terracottaConfiguration.getConsistency() == Consistency.EVENTUAL) {
      builder.consistency(org.terracotta.toolkit.store.ToolkitConfigFields.Consistency.EVENTUAL);
    } else {
      builder.consistency(org.terracotta.toolkit.store.ToolkitConfigFields.Consistency.STRONG);
    }

    if (terracottaConfiguration.getConcurrency() == TerracottaConfiguration.DEFAULT_CONCURRENCY) {
      builder.concurrency(calculateCorrectConcurrency(ehcacheConfig));
    } else {
      builder.concurrency(terracottaConfiguration.getConcurrency());
    }

    builder.localCacheEnabled(terracottaConfiguration.isLocalCacheEnabled());
    builder.configField(ConfigFieldsInternal.LOCAL_STORE_MANAGER_NAME_NAME, cacheManagerName);
    builder.pinnedInLocalMemory(isPinnedInLocalMemory(ehcacheConfig));
    builder.evictionEnabled(!isPinnedInCache(ehcacheConfig));
    builder.maxCountLocalHeap((int) ehcacheConfig.getMaxEntriesLocalHeap());
    builder.maxBytesLocalHeap(ehcacheConfig.getMaxBytesLocalHeap());
    builder.maxBytesLocalOffheap(ehcacheConfig.getMaxBytesLocalOffHeap());
    builder.offheapEnabled(ehcacheConfig.isOverflowToOffHeap());
    builder.compressionEnabled(terracottaConfiguration.isCompressionEnabled());
    builder.copyOnReadEnabled(ehcacheConfig.isCopyOnRead());

    return builder.build();
  }

  private static boolean isPinnedInCache(final CacheConfiguration ehcacheConfig) {
    return ehcacheConfig.getPinningConfiguration() != null
           && ehcacheConfig.getPinningConfiguration().getStore() == PinningConfiguration.Store.INCACHE;
  }

  private static int calculateCorrectConcurrency(CacheConfiguration cacheConfiguration) {
    int maxElementOnDisk = cacheConfiguration.getMaxElementsOnDisk();
    if (maxElementOnDisk <= 0 || maxElementOnDisk >= ToolkitConfigFields.DEFAULT_CONCURRENCY) { return ToolkitConfigFields.DEFAULT_CONCURRENCY; }
    int concurrency = 1;
    while (concurrency * 2 <= maxElementOnDisk) {// this while loop is not very time consuming, maximum it will do 8
      // iterations
      concurrency *= 2;
    }
    return concurrency;
  }

  private static boolean isPinnedInLocalMemory(CacheConfiguration ehcacheConfig) {
    return ehcacheConfig.getPinningConfiguration() != null
           && ehcacheConfig.getPinningConfiguration().getStore() == PinningConfiguration.Store.LOCALMEMORY;
  }

  @Override
  public String getFullyQualifiedCacheName(Ehcache cache) {
    return EhcacheEntitiesNaming.getToolkitCacheNameFor(getCacheManagerName(cache), cache.getName());
  }

  private static String getCacheManagerName(Ehcache cache) {
    final String cacheMgrName;
    if (cache.getCacheManager().isNamed()) {
      cacheMgrName = cache.getCacheManager().getName();
    } else {
      cacheMgrName = TerracottaClusteredInstanceFactory.DEFAULT_CACHE_MANAGER_NAME;
    }
    return cacheMgrName;
  }

  @Override
  public ToolkitLock getOrCreateStoreLock(Ehcache cache) {
    return toolkit.getLock(getFullyQualifiedCacheName(cache) + DELIMITER + "storeRWLock");
  }

  @Override
  public ToolkitMap getOrCreateExtractorsMap(final String cacheManagerName, String cacheName) {
    // implemented in ee version
    throw new UnsupportedOperationException();
  }

  @Override
  public ToolkitMap getOrCreateAttributeMap(final String cacheManagerName, String cacheName) {
    throw new UnsupportedOperationException();
  }

  @Override
  public void shutdown() {
    if (clusteredCacheManagerEntity != null) {
      try {
        clusteredCacheManagerEntity.releaseUse();
      } catch (Exception e) {
        // Ignore - will be shutting down toolkit anyway
        LOGGER.debug("Exception occurred while releasing clustered cache manager entity use", e);
      }
    }
    clusteredEntityManager.dispose();
    toolkit.shutdown();
  }

  @Override
  public SerializedToolkitCache getOrCreateAllSoftLockMap(String cacheManagerName,
                                                                                                                            String cacheName) {
    // TODO: what should be the local cache config for the map?
    Configuration config = new ToolkitStoreConfigBuilder()
        .consistency(org.terracotta.toolkit.store.ToolkitConfigFields.Consistency.STRONG).build();
    String softLockCacheName = EhcacheEntitiesNaming.getToolkitCacheNameFor(cacheManagerName, cacheName) + DELIMITER
                               + ALL_SOFT_LOCKS_MAP_SUFFIX;
    ToolkitCache map = toolkit
        .getCache(softLockCacheName, config, SerializedReadCommittedClusteredSoftLock.class);
    addCacheMetaInfo(cacheName, ToolkitObjectType.CACHE, softLockCacheName);
    return new SerializedToolkitCache(map);

  }

  @Override
  public ToolkitMap getOrCreateNewSoftLocksSet(String cacheManagerName,
                                                                                                  String cacheName) {
    String softLockMapName = EhcacheEntitiesNaming.getToolkitCacheNameFor(cacheManagerName, cacheName) + DELIMITER
                             + NEW_SOFT_LOCKS_LIST_SUFFIX;
    ToolkitMap softLockMap = toolkit
        .getMap(softLockMapName, SerializedReadCommittedClusteredSoftLock.class, Integer.class);
    addCacheMetaInfo(cacheName, ToolkitObjectType.MAP, softLockMapName);
    return softLockMap;
  }

  @Override
  public ToolkitMap getOrCreateAsyncConfigMap() {
    return toolkit.getMap(EhcacheEntitiesNaming.getAsyncConfigMapName(), String.class, AsyncConfig.class);
  }

  @Override
  public ToolkitMap> getOrCreateAsyncListNamesMap(String fullAsyncName, String cacheName) {
    ToolkitMap asyncListNames = toolkit.getMap(fullAsyncName, String.class, Set.class);
    addCacheMetaInfo(cacheName, ToolkitObjectType.MAP, fullAsyncName);
    addKeyRemoveInfo(cacheName, EhcacheEntitiesNaming.getAsyncConfigMapName(), fullAsyncName);
    return asyncListNames;
  }

  @Override
  public ToolkitListInternal getAsyncProcessingBucket(String bucketName, String cacheName) {
    ToolkitListInternal toolkitList = (ToolkitListInternal) toolkit.getList(bucketName, null);
    addCacheMetaInfo(cacheName, ToolkitObjectType.LIST, bucketName);
    return toolkitList;
  }

  @Override
  public ToolkitMap getOrCreateClusteredStoreConfigMap(String cacheManagerName, String cacheName) {
    String configMapName = EhcacheEntitiesNaming.getToolkitCacheNameFor(cacheManagerName, cacheName);
    ToolkitMap configMap = getOrCreateConfigMap(configMapName);
    addCacheMetaInfo(cacheName, ToolkitObjectType.MAP, configMapName);
    return configMap;
  }

  private ToolkitMap getOrCreateConfigMap(final String fullyQualifiedCacheName) {
    // TODO: what should be the local cache config for the map?
    return toolkit.getMap(EhcacheEntitiesNaming.getToolkitCacheConfigMapName(fullyQualifiedCacheName), String.class, Serializable.class);
  }

  @Override
  public SerializedToolkitCache getOrCreateTransactionCommitStateMap(String cacheManagerName) {
    // TODO: what should be the local cache config for the map?
    Configuration config = new ToolkitStoreConfigBuilder()
        .consistency(org.terracotta.toolkit.store.ToolkitConfigFields.Consistency.SYNCHRONOUS_STRONG).build();
    ToolkitCache map = toolkit.getCache(cacheManagerName + DELIMITER
                                                              + EHCACHE_TXNS_DECISION_STATE_MAP_NAME, config,
                                                          Decision.class);
    return new SerializedToolkitCache(map);
  }

  @Override
  public ToolkitLock getSoftLockWriteLock(String cacheManagerName, String cacheName, TransactionID transactionID,
                                          Object key) {

    return toolkit.getLock(EhcacheEntitiesNaming.getToolkitCacheNameFor(cacheManagerName, cacheName) + DELIMITER
                           + serializeToString(transactionID) + DELIMITER + serializeToString(key) + DELIMITER
                           + EHCACHE_TXNS_SOFTLOCK_WRITE_LOCK_NAME);
  }

  @Override
  public ToolkitLock getLockForCache(Ehcache cache, String lockName) {
    return toolkit.getLock(getFullyQualifiedCacheName(cache) + DELIMITER + lockName);
  }



  private static String serializeToString(Object serializable) {
    try {
      return SerializationHelper.serializeToString(serializable);
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

  @Override
  public ToolkitReadWriteLock getSoftLockFreezeLock(String cacheManagerName, String cacheName,
                                                    TransactionID transactionID, Object key) {

    return toolkit.getReadWriteLock(EhcacheEntitiesNaming.getToolkitCacheNameFor(cacheManagerName, cacheName) + DELIMITER
                                    + serializeToString(transactionID) + DELIMITER + serializeToString(key) + DELIMITER
                                    + EHCACHE_TXNS_SOFTLOCK_FREEZE_LOCK_NAME);
  }

  @Override
  public ToolkitReadWriteLock getSoftLockNotifierLock(String cacheManagerName, String cacheName,
                                                      TransactionID transactionID, Object key) {

    return toolkit.getReadWriteLock(EhcacheEntitiesNaming.getToolkitCacheNameFor(cacheManagerName, cacheName) + DELIMITER
                                    + serializeToString(transactionID) + DELIMITER + serializeToString(key) + DELIMITER
                                    + EHCACHE_TXNS_SOFTLOCK_NOTIFIER_LOCK_NAME);
  }

  @Override
  public boolean destroy(final String cacheManagerName, final String cacheName) {
    getOrCreateAllSoftLockMap(cacheManagerName, cacheName).destroy();
    getOrCreateNewSoftLocksSet(cacheManagerName, cacheName).destroy();
    getOrCreateCacheEventNotifier(cacheManagerName, cacheName).destroy();
    getOrCreateConfigChangeNotifier(cacheManagerName, cacheName).destroy();
    getOrCreateToolkitCache(EhcacheEntitiesNaming.getToolkitCacheNameFor(cacheManagerName, cacheName),
                            new ToolkitCacheConfigBuilder().maxCountLocalHeap(1).maxBytesLocalOffheap(0).build())
        .destroy();

    // We always write the transactional mode into this config map, so theoretically if the cache existed, this map
    // won't be empty.
    ToolkitMap clusteredStoreConfigMap = getOrCreateClusteredStoreConfigMap(cacheManagerName, cacheName);
    boolean existed = !clusteredStoreConfigMap.isEmpty();
    clusteredStoreConfigMap.destroy();

    return existed;
  }

  protected void addNonStopConfigForCache(final CacheConfiguration ehcacheConfig, final String fullyQualifiedCacheName) {
    final TerracottaConfiguration terracottaConfiguration = ehcacheConfig.getTerracottaConfiguration();
    ToolkitNonStopConfiguration nonstopConfiguration = new ToolkitNonStopConfiguration(
                                                                                       terracottaConfiguration
                                                                                           .getNonstopConfiguration());
    toolkit.getFeature(ToolkitFeatureType.NONSTOP).getNonStopConfigurationRegistry()
        .registerForInstance(nonstopConfiguration, fullyQualifiedCacheName, ToolkitObjectType.CACHE);
  }

  @Override
  public void removeNonStopConfigforCache(Ehcache cache) {
    toolkit.getFeature(ToolkitFeatureType.NONSTOP).getNonStopConfigurationRegistry()
        .deregisterForInstance(getFullyQualifiedCacheName(cache), ToolkitObjectType.CACHE);

  }

  protected void addCacheMetaInfo(String cacheName, ToolkitObjectType type, String dsName) {
    ToolkitBackedClusteredCacheManager tbccm = (ToolkitBackedClusteredCacheManager) clusteredCacheManagerEntity;
    tbccm.addCacheMetaInfo(cacheName, type, dsName);
  }

  private void addKeyRemoveInfo(String cacheName, String toolkitMapName, String keytoBeRemoved) {
    ToolkitBackedClusteredCacheManager tbccm = (ToolkitBackedClusteredCacheManager) clusteredCacheManagerEntity;
    tbccm.addKeyRemoveInfo(cacheName, toolkitMapName, keytoBeRemoved);
  }

  @Override
  public void linkClusteredCacheManager(String cacheManagerName, net.sf.ehcache.config.Configuration configuration) {
    if (clusteredCacheManagerEntity == null) {
      logCacheManagerConfigInTerracottaClientLogs(cacheManagerName, configuration);

      ClusteredCacheManager clusteredCacheManager = clusteredEntityManager.getRootEntity(cacheManagerName,
          ClusteredCacheManager.class);
      ToolkitReadWriteLock cmRWLock = clusteredEntityManager.getEntityLock(EhcacheEntitiesNaming
          .getCacheManagerLockNameFor(cacheManagerName));
      ToolkitLock cmWriteLock = cmRWLock.writeLock();
      while (clusteredCacheManager == null) {
        if (cmWriteLock.tryLock()) {
          try {
            clusteredCacheManager = createClusteredCacheManagerEntity(cacheManagerName, configuration);
          } finally {
            cmWriteLock.unlock();
          }
        } else {
          clusteredCacheManager = clusteredEntityManager.getRootEntity(cacheManagerName, ClusteredCacheManager.class);
        }
      }
      clusteredCacheManagerEntity = clusteredCacheManager;
      entityNames.setCacheManagerName(cacheManagerName);
    }
  }

  private void logCacheManagerConfigInTerracottaClientLogs(String cacheManagerName, net.sf.ehcache.config.Configuration configuration) {
    ToolkitLogger logger = ((ToolkitInternal)toolkit).getLogger(CONFIG_LOGGER_NAME);
    if (logger.isInfoEnabled()) {
      try {
        logger.info("Configuration for clustered cache manager " + cacheManagerName + ":\n" +
                    convertConfigurationToXMLString(configuration, cacheManagerName, true));
      } catch (Exception e) {
        logger.warn("Exception while trying to log configuration for clustered cache manager " + cacheManagerName, e);
      }
    }
  }

  private ClusteredCacheManager createClusteredCacheManagerEntity(String cacheManagerName, net.sf.ehcache.config.Configuration configuration) {
    ClusteredCacheManager clusteredCacheManager;
    String xmlConfig = convertConfigurationToXMLString(configuration, cacheManagerName, true);
    clusteredCacheManager = new ToolkitBackedClusteredCacheManager(cacheManagerName,
                                                                   new ClusteredCacheManagerConfiguration(xmlConfig));
    ClusteredCacheManager existing = clusteredEntityManager.addRootEntityIfAbsent(cacheManagerName, ClusteredCacheManager.class, clusteredCacheManager);
    if (existing != null) {
      clusteredCacheManager = existing;
    }
    return clusteredCacheManager;
  }

  @Override
  public ToolkitMap getOrCreateCacheManagerMetaInfoMap(String cacheManagerName) {
    String configMapName = EhcacheEntitiesNaming.getCacheManagerConfigMapName(cacheManagerName);
    ToolkitMap configMap = toolkit.getMap(configMapName, String.class, Serializable.class);
    return configMap;
  }

  void addCacheEntityInfo(final String cacheName, final CacheConfiguration ehcacheConfig, String toolkitCacheName) {
    if (clusteredCacheManagerEntity == null) {
      throw new IllegalStateException(format("ClusteredCacheManger entity not configured for cache %s", cacheName));
    }

    logCacheConfigInTerracottaClientLogs(cacheName, ehcacheConfig);

    ClusteredCache cacheEntity = clusteredCacheManagerEntity.getCache(cacheName);
    if (cacheEntity == null) {
      ToolkitReadWriteLock cacheRWLock = clusteredCacheManagerEntity.getCacheLock(cacheName);
      ToolkitLock cacheWriteLock = cacheRWLock.writeLock();
      while (cacheEntity == null) {
        if (cacheWriteLock.tryLock()) {
          try {
            cacheEntity = createClusteredCacheEntity(cacheName, ehcacheConfig, toolkitCacheName);
          } finally {
            cacheWriteLock.unlock();
          }
        } else {
          cacheEntity = clusteredCacheManagerEntity.getCache(cacheName);
        }
      }
    }
    // TODO check some config elements
    clusteredCacheManagerEntity.markCacheInUse(cacheEntity);
    entityNames.addCacheName(cacheName);
  }

  private void logCacheConfigInTerracottaClientLogs(String cacheName, CacheConfiguration ehcacheConfig) {
    ToolkitLogger logger = ((ToolkitInternal)toolkit).getLogger(CONFIG_LOGGER_NAME);
    if (logger.isInfoEnabled()) {
      try {
        logger.info("Client configuration for clustered cache named " + cacheName + ":\n(clustered properties may differ in runtime cache depending on configuration used at creation time)\n" +
                    convertCacheConfigurationToXMLString(ehcacheConfig));
      } catch (Exception e) {
        logger.warn("Exception while trying to log configuration for clustered cache " + cacheName, e);
      }
    }
  }

  private ClusteredCache createClusteredCacheEntity(String cacheName, CacheConfiguration ehcacheConfig, String toolkitCacheName) {
    ClusteredCacheConfiguration clusteredConfiguration = createClusteredCacheConfiguration(ehcacheConfig);
    ClusteredCache cacheEntity = new ToolkitBackedClusteredCache(cacheName, clusteredConfiguration, toolkitCacheName);

    ClusteredCache existing = clusteredCacheManagerEntity.addCacheIfAbsent(cacheName, cacheEntity);
    if (existing != null) {
      cacheEntity = existing;
    }
    return cacheEntity;
  }

  private ClusteredCacheConfiguration createClusteredCacheConfiguration(CacheConfiguration ehcacheConfig) {
    String xmlConfig = convertCacheConfigurationToXMLString(ehcacheConfig);
    return new ClusteredCacheConfiguration(xmlConfig);
  }

  private String convertCacheConfigurationToXMLString(CacheConfiguration ehcacheConfig) {
    net.sf.ehcache.config.Configuration configuration = parseCacheManagerConfiguration(clusteredCacheManagerEntity.getConfiguration()
        .getConfigurationAsText());
    return ConfigurationUtil.generateCacheConfigurationText(configuration, ehcacheConfig);
  }

  @Override
  public void unlinkCache(String cacheName) {
    entityNames.removeCacheName(cacheName);
    ClusteredCache cacheEntity = clusteredCacheManagerEntity.getCache(cacheName);
    clusteredCacheManagerEntity.releaseCacheUse(cacheEntity);
  }

  @Override
  public void clusterRejoined() {
    synchronized (entityNames) {
      String cacheManagerName = entityNames.cacheManagerName;
      ClusteredCacheManager clusteredCacheManager = clusteredEntityManager.getRootEntity(cacheManagerName,
                                                                                         ClusteredCacheManager.class);
      if (clusteredCacheManager == null) {
        LOGGER.error("Cache Manager {} has been destroyed by some other node - shutting down to prevent any further use of clustered features", cacheManagerName);
        shutdown();
      } else {
        // release Cache Manager lock after rejoin
        try {
          clusteredCacheManager.releaseUse();
        } catch (Exception e) {
          // Ignore - just trying to clean up
          LOGGER.trace("Exception trying to release cache manager {} after rejoin", entityNames.cacheManagerName);
        }

        // release cache read lock after rejoin
        for (String cacheName : entityNames.getCacheNames()) {
          ClusteredCache cacheEntity = clusteredCacheManagerEntity.getCache(cacheName);
          if (cacheEntity != null) {
            try {
              clusteredCacheManagerEntity.releaseCacheUse(cacheEntity);
            } catch (Exception e) {
              // Ignore - just trying to clean up
              LOGGER.trace("Exception trying to release cache {} after rejoin", cacheName);
            }
          }
        }

        int retryCount = 0;
        boolean success = false;
        while (retryCount < RETRY_MARK_IN_USE_AFTER_REJOIN) {
          // grab Cache Manager read lock after rejoin
          try {
            clusteredCacheManager.markInUse();
            success = (clusteredEntityManager.getRootEntity(cacheManagerName, ClusteredCacheManager.class) != null);
            break;
          } catch (Exception e) {
            try {
              TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e1) {
              // Do nothing
            }
            retryCount++;
          }
        }
        if (!success) {
          LOGGER.error("Unable to mark cache manager {} in use - shutting down to prevent any further use of clustered features", cacheManagerName);
          shutdown();
        } else {
          // grab cache read lock after rejoin
          for (String cacheName : entityNames.getCacheNames()) {
            boolean successCache = false;
            int retryCountCache = 0;
            while (!successCache && retryCountCache < RETRY_MARK_IN_USE_AFTER_REJOIN) {
              ClusteredCache cacheEntity = clusteredCacheManagerEntity.getCache(cacheName);
              if (cacheEntity == null) {
                LOGGER.error("Cache " + cacheName + " has been destroyed by some other node");
                successCache = true;
              } else {
                try {
                  clusteredCacheManagerEntity.markCacheInUse(cacheEntity);
                  successCache = true;
                } catch (Exception e) {
                  try {
                    TimeUnit.SECONDS.sleep(1);
                  } catch (InterruptedException e1) {
                    // Do nothing
                  }
                  retryCountCache++;
                }
              }
            }
          }
        }
      }
    }
  }

  private String convertConfigurationToXMLString(net.sf.ehcache.config.Configuration configuration, String cacheManagerName, boolean stripCacheConfigs) {
    net.sf.ehcache.config.Configuration targetConfiguration = cloneConfiguration(configuration);
    targetConfiguration.setName(cacheManagerName);
    if (stripCacheConfigs) {
      targetConfiguration.getCacheConfigurations().clear();
    }
    return ConfigurationUtil.generateCacheManagerConfigurationText(targetConfiguration);
  }

  private net.sf.ehcache.config.Configuration cloneConfiguration(net.sf.ehcache.config.Configuration configuration) {
    String tmp = ConfigurationUtil.generateCacheManagerConfigurationText(configuration);
    net.sf.ehcache.config.Configuration targetConfiguration;
    targetConfiguration = parseCacheManagerConfiguration(tmp);
    return targetConfiguration;
  }

  private net.sf.ehcache.config.Configuration parseCacheManagerConfiguration(String xmlCacheManagerConfig) {
    net.sf.ehcache.config.Configuration targetConfiguration;
    targetConfiguration = ConfigurationFactory.parseConfiguration(new BufferedInputStream(new ByteArrayInputStream(xmlCacheManagerConfig
        .getBytes())));
    return targetConfiguration;
  }

  private class EntityNamesHolder {
    private String            cacheManagerName;
    private final Set cacheNames;

    private EntityNamesHolder() {
      cacheNames = new HashSet();
    }

    private synchronized void setCacheManagerName(String cacheMgrName) {
      if (cacheManagerName == null) {
        cacheManagerName = cacheMgrName;
        ToolkitInstanceFactoryImpl.this.clusteredCacheManagerEntity.markInUse();
      }
    }

    private synchronized void addCacheName(String cacheName) {
      cacheNames.add(cacheName);
    }

    private synchronized void removeCacheName(String cacheName) {
      cacheNames.remove(cacheName);
    }

    private Set getCacheNames() {
      return Collections.unmodifiableSet(cacheNames);
    }
  }

  private static class EhcacheTcConfig {
    private enum Type {
      URL, EMBEDDED_TC_CONFIG, FILE
    }

    private final Type   type;
    private final String tcConfigUrlOrSnippet;

    private EhcacheTcConfig(Type type, String config) {
      this.type = type;
      this.tcConfigUrlOrSnippet = config;
    }

    public static EhcacheTcConfig create(TerracottaClientConfiguration config) {
      if (config.isUrlConfig()) {
        String urlOrFilePath = config.getUrl();
        if (isFile(urlOrFilePath)) {
          return new EhcacheTcConfig(Type.FILE, slurpFile(urlOrFilePath));
        } else if (isValidURL(urlOrFilePath)) {
          return new EhcacheTcConfig(Type.EMBEDDED_TC_CONFIG, fetchConfigFromURL(urlOrFilePath));
        } else {
          return new EhcacheTcConfig(Type.URL, urlOrFilePath);
        }
      } else {
        return new EhcacheTcConfig(Type.EMBEDDED_TC_CONFIG, config.getEmbeddedConfig());
      }
    }

    private static String slurpFile(String urlOrFilePath) {
      try {
        return fetchConfigFromStream(new FileInputStream(urlOrFilePath));
      } catch (FileNotFoundException e) {
        throw new RuntimeException(e);
      }
    }

    private static boolean isFile(String urlOrFilePath) {
      File file = new File(urlOrFilePath);
      return file.exists() && file.isFile();
    }

    private static String fetchConfigFromURL(String urlOrFilePath) {
      try {
        return fetchConfigFromStream(new URL(urlOrFilePath).openStream());
      } catch (MalformedURLException e) {
        throw new RuntimeException(e);
      } catch (IOException e) {
        throw new RuntimeException(e);
      }
    }

    private static String fetchConfigFromStream(InputStream inputStream) {
      try {
        StringBuilder builder = new StringBuilder();
        BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
        String line = null;
        while ((line = br.readLine()) != null) {
          builder.append(line);
        }
        return builder.toString();
      } catch (IOException e) {
        throw new RuntimeException(e);
      }
    }

    private static boolean isValidURL(String urlOrFilePath) {
      try {
        new URL(urlOrFilePath);
        return true;
      } catch (MalformedURLException e) {
        return false;
      }
    }

  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy