class Cache implements InternalEhcache, StoreListener {

    public void initialise() {
        synchronized (this) {
            final ClassLoader loader = getCacheConfiguration().getClassLoader();
            
          
            // verify that the cache and cache manager use the same classloader reference
            if (loader != cacheManager.getConfiguration().getClassLoader()) {
                
                // XXX: Is there a better way to relax this check for shadow caches?
                if (!getName().startsWith(CacheManager.LOCAL_CACHE_NAME_PREFIX)) {                              
                    throw new CacheException("This cache (" + getName() + ") is configurated with a different classloader reference than its containing cache manager");                               
                }
            }
            
            if (!cacheStatus.canInitialize()) {
                throw new IllegalStateException("Cannot initialise the " + configuration.getName()
                        + " cache because its status is not STATUS_UNINITIALISED");
            }

            // on-heap pool configuration
            final Pool onHeapPool;
            if (configuration.getMaxBytesLocalHeap() > 0) {
                PoolEvictor evictor = new FromLargestCachePoolEvictor();
                SizeOfEngine sizeOfEngine = cacheManager.createSizeOfEngine(this);
                onHeapPool = new BoundedPool(configuration.getMaxBytesLocalHeap(), evictor, sizeOfEngine);
            } else if (getCacheManager() != null && getCacheManager().getConfiguration().isMaxBytesLocalHeapSet()) {
                onHeapPool = getCacheManager().getOnHeapPool();
            } else {
                onHeapPool = new UnboundedPool();
            }

            // on-disk pool configuration
            final Pool onDiskPool;
            if (configuration.getMaxBytesLocalDisk() > 0) {
                PoolEvictor evictor = new FromLargestCachePoolEvictor();
                onDiskPool = new BoundedPool(configuration.getMaxBytesLocalDisk(), evictor, null);
            } else if (getCacheManager() != null && getCacheManager().getConfiguration().isMaxBytesLocalDiskSet()) {
                onDiskPool = getCacheManager().getOnDiskPool();
            } else {
                onDiskPool = new UnboundedPool();
            }
            /*We don't have to worry about the old value as when we are called the CacheConfiguration should
             have validated and resized the Cachemanager Pool as CacheConfiguration adds itself as first listener.
              so we just handle heap and disk pools resizing.*/
            this.configuration.addConfigurationListener(new AbstractCacheConfigurationListener() {
                @Override
                public void maxBytesLocalHeapChanged(long oldValue, long newValue) {

                    onHeapPool.setMaxSize(newValue);
                }
                @Override
                public void maxBytesLocalDiskChanged(long oldValue, long newValue) {
                    onDiskPool.setMaxSize(newValue);
                }
            });

            Store store;
            if (isTerracottaClustered()) {
                checkClusteredConfig();
                int maxConcurrency = Integer.getInteger(EHCACHE_CLUSTERREDSTORE_MAX_CONCURRENCY_PROP,
                        DEFAULT_EHCACHE_CLUSTERREDSTORE_MAX_CONCURRENCY);
                if (getCacheConfiguration().getTerracottaConfiguration().getConcurrency() > maxConcurrency) {
                    throw new InvalidConfigurationException("Maximum supported concurrency for Terracotta clustered caches is "
                            + maxConcurrency + ". Please reconfigure cache '" + getName() + "' with concurrency value <= " + maxConcurrency
                            + " or use system property '" + EHCACHE_CLUSTERREDSTORE_MAX_CONCURRENCY_PROP + "' to override the default");
                }
                elementValueComparator = configuration.getElementValueComparatorConfiguration().createElementComparatorInstance(configuration, loader);

                Callable<TerracottaStore> callable = new Callable<TerracottaStore>() {
                    @Override
                    public TerracottaStore call() throws Exception {
                        cacheManager.getClusteredInstanceFactory().linkClusteredCacheManager(cacheManager.getName(), cacheManager.getConfiguration());
                        Store tempStore = null;
                        try {
                            tempStore = cacheManager.createTerracottaStore(Cache.this);
                        } catch (IllegalArgumentException e) {
                            handleExceptionInTerracottaStoreCreation(e);
                        }
                        if (!(tempStore instanceof TerracottaStore)) {
                            throw new CacheException(
                                    "CacheManager should create instances of TerracottaStore for Terracotta Clustered caches instead of - "
                                            + (tempStore == null ? "null" : tempStore.getClass().getName()));
                        }

                        CacheConfiguration.TransactionalMode clusteredTransactionalMode = ((TerracottaStore) tempStore)
                                .getTransactionalMode();
                        if (clusteredTransactionalMode != null
                                && !clusteredTransactionalMode.equals(getCacheConfiguration().getTransactionalMode())) {
                            throw new InvalidConfigurationException("Transactional mode cannot be changed on clustered caches. "
                                    + "Please reconfigure cache '" + getName() + "' with transactionalMode = " + clusteredTransactionalMode);
                        }
                        
                        TerracottaStore terracottaStore = makeClusteredTransactionalIfNeeded((TerracottaStore) tempStore, elementValueComparator, loader);

                        if (isSearchable()) {
                            Map<String, AttributeExtractor> extractors = new HashMap<String, AttributeExtractor>();
                            for (SearchAttribute sa : configuration.getSearchAttributes().values()) {
                                extractors.put(sa.getName(), sa.constructExtractor(loader));
                            }

                            terracottaStore.setAttributeExtractors(extractors);
                        }
                        return terracottaStore;
                    }
                };

                NonstopConfiguration nonstopConfig = getCacheConfiguration().getTerracottaConfiguration().getNonstopConfiguration();
                // freeze the config whether nonstop is enabled or not
                if (nonstopConfig != null) {
                    nonstopConfig.freezeConfig();
                }

                store = cacheManager.getClusteredInstanceFactory().createNonStopStore(callable, this);
                clusterStateListener = new CacheClusterStateStatisticsListener(this);
                getCacheCluster().addTopologyListener(clusterStateListener);
            } else {
                FeaturesManager featuresManager = cacheManager.getFeaturesManager();
                if (featuresManager == null) {
                    if (configuration.isOverflowToOffHeap()) {
                        throw new CacheException("Cache " + configuration.getName()
                                + " cannot be configured because the enterprise features manager could not be found. "
                                + "You must use an enterprise version of Ehcache to successfully enable overflowToOffHeap.");
                    }
                    PersistenceConfiguration persistence = configuration.getPersistenceConfiguration();
                    if (persistence != null && Strategy.LOCALRESTARTABLE.equals(persistence.getStrategy())) {
                        throw new CacheException("Cache " + configuration.getName()
                                + " cannot be configured because the enterprise features manager could not be found. "
                                + "You must use an enterprise version of Ehcache to successfully enable enterprise persistence.");
                    }

                    if (useClassicLru && configuration.getMemoryStoreEvictionPolicy().equals(MemoryStoreEvictionPolicy.LRU)) {
                        Store disk = createDiskStore();
                        store = new LegacyStoreWrapper(new LruMemoryStore(this, disk), disk, registeredEventListeners, configuration);
                    } else {
                        if (configuration.isOverflowToDisk()) {
                            store = DiskStore.createCacheStore(this, onHeapPool, onDiskPool);
                        } else {
                            store = MemoryStore.create(this, onHeapPool);
                        }
                    }
                } else {
                    store = featuresManager.createStore(this, onHeapPool, onDiskPool);
                }
                store = handleTransactionalAndCopy(store, loader);
            }


            this.compoundStore = store;

            if (!isTerracottaClustered() && isSearchable()) {
                Map<String, AttributeExtractor> extractors = new HashMap<String, AttributeExtractor>();
                for (SearchAttribute sa : configuration.getSearchAttributes().values()) {
                    extractors.put(sa.getName(), sa.constructExtractor(loader));
                }

                compoundStore.setAttributeExtractors(extractors);
            }
            this.cacheWriterManager = configuration.getCacheWriterConfiguration().getWriteMode().createWriterManager(this, compoundStore);
            StatisticsManager.associate(this).withChild(cacheWriterManager);
            cacheStatus.changeState(Status.STATUS_ALIVE);
            initialiseRegisteredCacheWriter();
            initialiseCacheWriterManager(false);
            initialiseRegisteredCacheExtensions();
            initialiseRegisteredCacheLoaders();

            Object context = compoundStore.getInternalContext();
            if (context instanceof CacheLockProvider) {
                lockProvider = (CacheLockProvider) context;
            } else {
                this.lockProvider = new StripedReadWriteLockSync(StripedReadWriteLockSync.DEFAULT_NUMBER_OF_MUTEXES);
            }

            StatisticsManager.associate(this).withChild(compoundStore);
            statistics = new StatisticsGateway(this, cacheManager.getStatisticsExecutor());
        }

        if (!isTerracottaClustered()) {
            compoundStore.addStoreListener(this);
        }

        if (LOG.isDebugEnabled()) {
            LOG.debug("Initialised cache: " + configuration.getName());
        }

        if (disabled) {
            LOG.warn("Cache: " + configuration.getName() + " is disabled because the " + NET_SF_EHCACHE_DISABLED
                    + " property was set to true. No elements will be added to the cache.");
        }
    }

    public  Cache(CacheConfiguration cacheConfiguration);
    public  Cache(CacheConfiguration cacheConfiguration,
                 RegisteredEventListeners registeredEventListeners,
                 BootstrapCacheLoader bootstrapCacheLoader);
    public  Cache(String name, int maxElementsInMemory, boolean overflowToDisk,
                 boolean eternal, long timeToLiveSeconds, long timeToIdleSeconds);
    public  Cache(String name,
                 int maxElementsInMemory,
                 boolean overflowToDisk,
                 boolean eternal,
                 long timeToLiveSeconds,
                 long timeToIdleSeconds,
                 boolean diskPersistent,
                 long diskExpiryThreadIntervalSeconds);
    public  Cache(String name,
                 int maxElementsInMemory,
                 MemoryStoreEvictionPolicy memoryStoreEvictionPolicy,
                 boolean overflowToDisk,
                 String diskStorePath,
                 boolean eternal,
                 long timeToLiveSeconds,
                 long timeToIdleSeconds,
                 boolean diskPersistent,
                 long diskExpiryThreadIntervalSeconds,
                 RegisteredEventListeners registeredEventListeners);
    public  Cache(String name,
                 int maxElementsInMemory,
                 MemoryStoreEvictionPolicy memoryStoreEvictionPolicy,
                 boolean overflowToDisk,
                 String diskStorePath,
                 boolean eternal,
                 long timeToLiveSeconds,
                 long timeToIdleSeconds,
                 boolean diskPersistent,
                 long diskExpiryThreadIntervalSeconds,
                 RegisteredEventListeners registeredEventListeners,
                 BootstrapCacheLoader bootstrapCacheLoader);
    public  Cache(String name,
                 int maxElementsInMemory,
                 MemoryStoreEvictionPolicy memoryStoreEvictionPolicy,
                 boolean overflowToDisk,
                 String diskStorePath,
                 boolean eternal,
                 long timeToLiveSeconds,
                 long timeToIdleSeconds,
                 boolean diskPersistent,
                 long diskExpiryThreadIntervalSeconds,
                 RegisteredEventListeners registeredEventListeners,
                 BootstrapCacheLoader bootstrapCacheLoader,
                 int maxElementsOnDisk);
    public  Cache(String name,
                 int maxElementsInMemory,
                 MemoryStoreEvictionPolicy memoryStoreEvictionPolicy,
                 boolean overflowToDisk,
                 String diskStorePath,
                 boolean eternal,
                 long timeToLiveSeconds,
                 long timeToIdleSeconds,
                 boolean diskPersistent,
                 long diskExpiryThreadIntervalSeconds,
                 RegisteredEventListeners registeredEventListeners,
                 BootstrapCacheLoader bootstrapCacheLoader,
                 int maxElementsOnDisk,
                 int diskSpoolBufferSizeMB);
    public  Cache(String name,
                 int maxElementsInMemory,
                 MemoryStoreEvictionPolicy memoryStoreEvictionPolicy,
                 boolean overflowToDisk,
                 String diskStorePath,
                 boolean eternal,
                 long timeToLiveSeconds,
                 long timeToIdleSeconds,
                 boolean diskPersistent,
                 long diskExpiryThreadIntervalSeconds,
                 RegisteredEventListeners registeredEventListeners,
                 BootstrapCacheLoader bootstrapCacheLoader,
                 int maxElementsOnDisk,
                 int diskSpoolBufferSizeMB,
                 boolean clearOnFlush);
    public  Cache(String name, int maxElementsInMemory, MemoryStoreEvictionPolicy memoryStoreEvictionPolicy, boolean overflowToDisk,
                 String diskStorePath, boolean eternal, long timeToLiveSeconds, long timeToIdleSeconds, boolean diskPersistent,
                 long diskExpiryThreadIntervalSeconds, RegisteredEventListeners registeredEventListeners,
                 BootstrapCacheLoader bootstrapCacheLoader, int maxElementsOnDisk, int diskSpoolBufferSizeMB, boolean clearOnFlush,
                 boolean isTerracottaClustered, boolean terracottaCoherentReads);
      Cache(CacheConfiguration config, Store compoundStore, RegisteredEventListeners listeners);
    private  Cache(Cache original);

}