net.sf.ehcache.CacheTest.java Source code

Java tutorial

Introduction

Here is the source code for net.sf.ehcache.CacheTest.java

Source

/**
 *  Copyright 2003-2007 Luck Consulting Pty Ltd
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

package net.sf.ehcache;

import edu.emory.mathcs.backport.java.util.concurrent.ExecutionException;
import edu.emory.mathcs.backport.java.util.concurrent.ExecutorService;
import edu.emory.mathcs.backport.java.util.concurrent.Future;
import net.sf.ehcache.bootstrap.BootstrapCacheLoader;
import net.sf.ehcache.event.RegisteredEventListeners;
import net.sf.ehcache.exceptionhandler.ExceptionHandlingDynamicCacheProxy;
import net.sf.ehcache.loader.CountingCacheLoader;
import net.sf.ehcache.loader.ExceptionThrowingLoader;
import net.sf.ehcache.loader.CacheLoader;
import net.sf.ehcache.store.MemoryStoreEvictionPolicy;
import net.sf.jsr107cache.*;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.io.ByteArrayInputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Random;
import java.util.Collection;
import java.util.Map;

/**
 * Tests for a Cache
 *
 * @author Greg Luck, Claus Ibsen
 * @version $Id: CacheTest.java 703 2008-07-12 03:50:09Z gregluck $
 */
public class CacheTest extends AbstractCacheTest {
    private static final Log LOG = LogFactory.getLog(CacheTest.class.getName());

    /**
     * teardown
     */
    protected void tearDown() throws Exception {
        super.tearDown();
    }

    /**
     * Gets the sample cache 1
     */
    protected Ehcache getSampleCache1() {
        Cache cache = manager.getCache("sampleCache1");
        return cache;
    }

    /**
     * Creates a cache
     *
     * @return
     */
    protected Ehcache createTestCache() {
        Cache cache = new Cache("test4", 1000, true, true, 0, 0);
        manager.addCache(cache);
        return cache;
    }

    /**
     * Checks we cannot use a cache after shutdown
     */
    public void testUseCacheAfterManagerShutdown() throws CacheException {
        Ehcache cache = getSampleCache1();
        manager.shutdown();
        Element element = new Element("key", "value");
        try {
            cache.getSize();
            fail();
        } catch (IllegalStateException e) {
            assertEquals("The sampleCache1 Cache is not alive.", e.getMessage());
        }
        try {
            cache.put(element);
            fail();
        } catch (IllegalStateException e) {
            assertEquals("The sampleCache1 Cache is not alive.", e.getMessage());
        }
        try {
            cache.get("key");
            fail();
        } catch (IllegalStateException e) {
            assertEquals("The sampleCache1 Cache is not alive.", e.getMessage());
        }
        if (cache instanceof Cache) {
            Cache castCache = (Cache) cache;
            //ok to get stats
            castCache.getHitCount();
            castCache.getMemoryStoreHitCount();
            castCache.getDiskStoreHitCount();
            castCache.getMissCountExpired();
            castCache.getMissCountNotFound();
        }

    }

    /**
     * Checks we cannot use a cache outside the manager
     */
    public void testUseCacheOutsideManager() throws CacheException {
        //Not put into manager.
        Cache cache = new Cache("testCache", 1, true, false, 5, 2);
        Element element = new Element("key", "value");
        try {
            cache.getSize();
            fail();
        } catch (IllegalStateException e) {
            assertEquals("The testCache Cache is not alive.", e.getMessage());
        }
        try {
            cache.put(element);
            fail();
        } catch (IllegalStateException e) {
            assertEquals("The testCache Cache is not alive.", e.getMessage());
        }
        try {
            cache.get("key");
            fail();
        } catch (IllegalStateException e) {
            assertEquals("The testCache Cache is not alive.", e.getMessage());
        }
        //ok to get stats
        cache.getHitCount();
        cache.getMemoryStoreHitCount();
        cache.getDiskStoreHitCount();
        cache.getMissCountExpired();
        cache.getMissCountNotFound();
    }

    /**
     * Checks when and how we can set the cache name.
     */
    public void testSetCacheName() throws CacheException {
        //Not put into manager.
        Ehcache cache = new Cache("testCache", 1, true, false, 5, 2);

        try {
            cache.setName(null);
            fail();
        } catch (IllegalArgumentException e) {
            //expected
        }

        try {
            cache.setName("illegal/name");
            fail();
        } catch (IllegalArgumentException e) {
            //expected
        }

        manager.addCache(cache);
        try {
            cache.setName("trying_to_change_name_after_initialised");
            fail();
        } catch (IllegalStateException e) {
            //expected
        }
    }

    /**
     * Test using a cache which has been removed and replaced.
     */
    public void testStaleCacheReference() throws CacheException {
        manager.addCache("test");
        Ehcache cache = manager.getCache("test");
        assertNotNull(cache);
        cache.put(new Element("key1", "value1"));

        assertEquals("value1", cache.get("key1").getObjectValue());
        manager.removeCache("test");
        manager.addCache("test");

        try {
            cache.get("key1");
            fail();
        } catch (IllegalStateException e) {
            assertEquals("The test Cache is not alive.", e.getMessage());
        }
    }

    /**
     * Tests getting the cache name
     *
     * @throws Exception
     */
    public void testCacheName() throws Exception {
        manager.addCache("test");
        Ehcache cache = manager.getCache("test");
        assertEquals("test", cache.getName());
        assertEquals(Status.STATUS_ALIVE, cache.getStatus());
    }

    /**
     * Tests getting the cache name
     *
     * @throws Exception
     */
    public void testCacheWithNoIdle() throws Exception {
        Ehcache cache = manager.getCache("sampleCacheNoIdle");
        assertEquals("sampleCacheNoIdle", cache.getName());
        assertEquals(Status.STATUS_ALIVE, cache.getStatus());
        assertEquals(0, cache.getTimeToIdleSeconds());
    }

    /**
     * Test expiry based on time to live
     * <cache name="sampleCacheNoIdle"
     * maxElementsInMemory="1000"
     * eternal="false"
     * timeToLiveSeconds="5"
     * overflowToDisk="false"
     * />
     */
    public void testExpiryBasedOnTimeToLiveWhenNoIdle() throws Exception {
        //Set size so the second element overflows to disk.
        Ehcache cache = manager.getCache("sampleCacheNoIdle");
        cache.put(new Element("key1", "value1"));
        cache.put(new Element("key2", "value1"));
        assertNotNull(cache.get("key1"));
        assertNotNull(cache.get("key2"));

        //Test time to idle. Should not idle out because not specified
        Thread.sleep(2000);
        assertNotNull(cache.get("key1"));
        assertNotNull(cache.get("key2"));

        //Test time to live.
        Thread.sleep(5020);
        assertNull(cache.get("key1"));
        assertNull(cache.get("key2"));
    }

    /**
     * When to search the disk store
     */
    public void testOverflowToDiskAndDiskPersistent() throws Exception {
        Ehcache cache = manager.getCache("sampleIdlingExpiringCache");

        for (int i = 0; i < 1001; i++) {
            cache.put(new Element("key" + i, "value1"));
        }

        assertNotNull(cache.get("key0"));

        for (int i = 0; i < 1001; i++) {
            cache.put(new Element("key" + i, "value1"));
            assertNotNull(cache.get("key" + i));
        }
    }

    /**
     * Test expiry based on time to live for a cache with config
     * <cache name="sampleCacheNoIdle"
     * maxElementsInMemory="1000"
     * eternal="false"
     * timeToLiveSeconds="5"
     * overflowToDisk="false"
     * />
     * <p/>
     * where an Elment override is set on TTL
     */
    public void testExpiryBasedOnTimeToLiveWhenNoIdleElementOverride() throws Exception {
        //Set size so the second element overflows to disk.
        Ehcache cache = manager.getCache("sampleCacheNoIdle");
        Element element1 = new Element("key1", "value1");
        element1.setTimeToLive(3);
        cache.put(element1);

        Element element2 = new Element("key2", "value1");
        element2.setTimeToLive(3);
        cache.put(element2);
        assertNotNull(cache.get("key1"));
        assertNotNull(cache.get("key2"));

        //Test time to idle. Should not idle out because not specified
        Thread.sleep(1000);
        assertNotNull(cache.get("key1"));
        assertNotNull(cache.get("key2"));

        //Test time to live.
        Thread.sleep(4020);
        assertNull(cache.get("key1"));
        assertNull(cache.get("key2"));
    }

    /**
     * Test expiry based on time to live for a cache with config
     * <cache name="sampleCacheNoIdle"
     * maxElementsInMemory="1000"
     * eternal="false"
     * timeToLiveSeconds="5"
     * overflowToDisk="false"
     * />
     * <p/>
     * where an Elment override is set on TTL
     */
    public void testExpiryBasedOnTimeToIdleElementOverride() throws Exception {
        //Set size so the second element overflows to disk.
        Ehcache cache = manager.getCache("sampleCacheNoIdle");
        assertEquals(30, cache.getCacheConfiguration().getDiskSpoolBufferSizeMB());
        Element element1 = new Element("key1", "value1");
        element1.setTimeToIdle(1);
        cache.put(element1);

        Element element2 = new Element("key2", "value1");
        element2.setTimeToIdle(1);
        cache.put(element2);
        assertNotNull(cache.get("key1"));
        assertNotNull(cache.get("key2"));

        //Test time to idle. Should not idle out because not specified
        Thread.sleep(1050);
        assertNull(cache.get("key1"));
        assertNull(cache.get("key2"));

    }

    /**
     * Test expiry based on time to live for a cache with config
     * <cache name="sampleCacheNoIdle"
     * maxElementsInMemory="1000"
     * eternal="false"
     * timeToLiveSeconds="5"
     * overflowToDisk="false"
     * />
     * <p/>
     * where an Elment override is set on TTL
     */
    public void testExpiryBasedEternalElementOverride() throws Exception {
        //Set size so the second element overflows to disk.
        Ehcache cache = manager.getCache("sampleCacheNoIdle");
        Element element1 = new Element("key1", "value1");
        element1.setEternal(true);
        cache.put(element1);

        Element element2 = new Element("key2", "value1");
        element2.setEternal(true);
        cache.put(element2);
        assertNotNull(cache.get("key1"));
        assertNotNull(cache.get("key2"));

        Thread.sleep(5050);
        assertNotNull(cache.get("key1"));
        assertNotNull(cache.get("key2"));

    }

    /**
     * Test expiry based on time to live. Even though eternal is false, because there are no
     * expiry or idle times, it is eternal.
     * <cache name="sampleCacheNotEternalButNoIdleOrExpiry"
     * maxElementsInMemory="1000"
     * eternal="false"
     * overflowToDisk="false"
     * />
     */
    public void testExpirySampleCacheNotEternalButNoIdleOrExpiry() throws Exception {
        //Set size so the second element overflows to disk.
        Ehcache cache = manager.getCache("sampleCacheNotEternalButNoIdleOrExpiry");
        cache.put(new Element("key1", "value1"));
        cache.put(new Element("key2", "value1"));
        assertNotNull(cache.get("key1"));
        assertNotNull(cache.get("key2"));

        //Test time to idle. Should not idle out because not specified
        Thread.sleep(2000);
        assertNotNull(cache.get("key1"));
        assertNotNull(cache.get("key2"));

        //Test time to live.
        Thread.sleep(5020);
        assertNotNull(cache.get("key1"));
        assertNotNull(cache.get("key2"));
    }

    /**
     * Test overflow to disk = false
     */
    public void testNoOverflowToDisk() throws Exception {
        //Set size so the second element overflows to disk.
        Cache cache = new Cache("test", 1, false, true, 5, 2);
        manager.addCache(cache);
        cache.put(new Element("key1", "value1"));
        cache.put(new Element("key2", "value1"));
        assertNull(cache.get("key1"));
        assertNotNull(cache.get("key2"));
    }

    /**
     * Performance tests for a range of Memory Store - Disk Store combinations.
     * <p/>
     * This demonstrates that a memory only store is approximately an order of magnitude
     * faster than a disk only store.
     * <p/>
     * It also shows that double the performance of a Disk Only store can be obtained
     * with a maximum memory size of only 1. Accordingly a Cache created without a
     * maximum memory size of less than 1 will issue a warning.
     * <p/>
     * Threading changes were made in v1.41 of DiskStore. The before and after numbers are shown.
     * <p/>
     * This test also has a cache with a CacheExceptionHandler registered. The performance effect is not detectable.
     */
    public void testProportionMemoryAndDiskPerformance() throws Exception {
        StopWatch stopWatch = new StopWatch();
        long time = 0;

        //Memory only Typical 192ms
        Cache memoryOnlyCache = new Cache("testMemoryOnly", 5000, false, false, 5, 2);
        manager.addCache(memoryOnlyCache);
        time = stopWatch.getElapsedTime();
        for (int i = 0; i < 5000; i++) {
            Integer key = new Integer(i);
            memoryOnlyCache.put(new Element(new Integer(i), "value"));
            memoryOnlyCache.get(key);
        }
        time = stopWatch.getElapsedTime();
        LOG.info("Time for MemoryStore: " + time);
        assertTrue("Time to put and get 5000 entries into MemoryStore", time < 300);

        //Memory only Typical 192ms
        for (int j = 0; j < 10; j++) {
            time = stopWatch.getElapsedTime();
            for (int i = 0; i < 5000; i++) {
                Integer key = new Integer(i);
                memoryOnlyCache.put(new Element(new Integer(i), "value"));
                memoryOnlyCache.get(key);
            }
            time = stopWatch.getElapsedTime();
            LOG.info("Time for MemoryStore: " + time);
            assertTrue("Time to put and get 5000 entries into MemoryStore", time < 300);
            Thread.sleep(500);
        }

        //Memory only with ExceptionHandlingTypical 192ms
        manager.replaceCacheWithDecoratedCache(memoryOnlyCache,
                ExceptionHandlingDynamicCacheProxy.createProxy(memoryOnlyCache));
        Ehcache exceptionHandlingMemoryOnlyCache = manager.getEhcache("testMemoryOnly");
        for (int j = 0; j < 10; j++) {
            time = stopWatch.getElapsedTime();
            for (int i = 0; i < 5000; i++) {
                Integer key = new Integer(i);
                exceptionHandlingMemoryOnlyCache.put(new Element(new Integer(i), "value"));
                exceptionHandlingMemoryOnlyCache.get(key);
            }
            time = stopWatch.getElapsedTime();
            LOG.info("Time for exception handling MemoryStore: " + time);
            assertTrue("Time to put and get 5000 entries into exception handling MemoryStore", time < 300);
            Thread.sleep(500);
        }

        //Set size so that all elements overflow to disk.
        // 1245 ms v1.38 DiskStore
        // 273 ms v1.42 DiskStore
        Cache diskOnlyCache = new Cache("testDiskOnly", 0, true, false, 5, 2);
        manager.addCache(diskOnlyCache);
        time = stopWatch.getElapsedTime();
        for (int i = 0; i < 5000; i++) {
            Integer key = new Integer(i);
            diskOnlyCache.put(new Element(key, "value"));
            diskOnlyCache.get(key);
        }
        time = stopWatch.getElapsedTime();
        LOG.info("Time for DiskStore: " + time);
        assertTrue("Time to put and get 5000 entries into DiskStore was less than 2 sec", time < 2000);

        // 1 Memory, 999 Disk
        // 591 ms v1.38 DiskStore
        // 56 ms v1.42 DiskStore
        Cache m1d999Cache = new Cache("m1d999Cache", 1, true, false, 5, 2);
        manager.addCache(m1d999Cache);
        time = stopWatch.getElapsedTime();
        for (int i = 0; i < 5000; i++) {
            Integer key = new Integer(i);
            m1d999Cache.put(new Element(key, "value"));
            m1d999Cache.get(key);
        }
        time = stopWatch.getElapsedTime();
        LOG.info("Time for m1d999Cache: " + time);
        assertTrue("Time to put and get 5000 entries into m1d999Cache", time < 2000);

        // 500 Memory, 500 Disk
        // 669 ms v1.38 DiskStore
        // 47 ms v1.42 DiskStore
        Cache m500d500Cache = new Cache("m500d500Cache", 500, true, false, 5, 2);
        manager.addCache(m500d500Cache);
        time = stopWatch.getElapsedTime();
        for (int i = 0; i < 5000; i++) {
            Integer key = new Integer(i);
            m500d500Cache.put(new Element(key, "value"));
            m500d500Cache.get(key);
        }
        time = stopWatch.getElapsedTime();
        LOG.info("Time for m500d500Cache: " + time);
        assertTrue("Time to put and get 5000 entries into m500d500Cache", time < 2000);

    }

    /**
     * Test Caches with persistent stores dispose properly. Tests:
     * <ol>
     * <li>No exceptions are thrown on dispose
     * <li>You cannot re add a cache after it has been disposed and removed
     * <li>You can create a new cache with the same name
     * </ol>
     */
    public void testCreateAddDisposeAdd() throws CacheException {
        Cache cache = new Cache("test2", 1, true, true, 0, 0, true, 120);
        manager.addCache(cache);
        cache.put(new Element("key1", "value1"));
        cache.put(new Element("key2", "value1"));
        int sizeFromGetSize = cache.getSize();
        int sizeFromKeys = cache.getKeys().size();
        assertEquals(sizeFromGetSize, sizeFromKeys);
        assertEquals(2, cache.getSize());
        //package protected method, only available to tests. Called by teardown
        cache.dispose();
        manager.removeCache("test2");

        try {
            manager.addCache(cache);
            fail();
        } catch (CacheException e) {
            //expected
        }

        //Add a new cache with the same name as the disposed one.
        Cache cache2 = new Cache("test2", 1, true, true, 0, 0, true, 120);
        manager.addCache(cache2);
        Ehcache cacheFromManager = manager.getCache("test2");
        assertTrue(cacheFromManager.getStatus().equals(Status.STATUS_ALIVE));

    }

    /**
     * Test expiry based on time to live
     */
    public void testExpiryBasedOnTimeToLive() throws Exception {
        //Set size so the second element overflows to disk.
        Cache cache = new Cache("test", 1, true, false, 3, 0);
        manager.addCache(cache);
        cache.put(new Element("key1", "value1"));
        cache.put(new Element("key2", "value1"));

        //Test time to live
        assertNotNull(cache.get("key1"));
        assertNotNull(cache.get("key2"));
        Thread.sleep(1020);
        //Test time to live
        assertNotNull(cache.get("key1"));
        assertNotNull(cache.get("key2"));
        Thread.sleep(1020);
        //Test time to live
        assertNotNull(cache.get("key1"));
        assertNotNull(cache.get("key2"));
        Thread.sleep(1020);
        assertNull(cache.get("key1"));
        assertNull(cache.get("key2"));
    }

    /**
     * Tests that a cache created from defaults will expire as per
     * the default expiry policy.
     *
     * @throws Exception
     */
    public void testExpiryBasedOnTimeToLiveForDefault() throws Exception {
        String name = "ThisIsACacheWhichIsNotConfiguredAndWillThereforeUseDefaults";
        Ehcache cache = null;
        CacheManager manager = CacheManager.getInstance();
        cache = manager.getCache(name);
        if (cache == null) {
            LOG.warn("Could not find configuration for " + name + ". Configuring using the defaultCache settings.");
            manager.addCache(name);
            cache = manager.getCache(name);
        }

        cache.put(new Element("key1", "value1"));
        cache.put(new Element("key2", "value1"));

        //Test time to live
        assertNotNull(cache.get("key1"));
        assertNotNull(cache.get("key2"));
        Thread.sleep(10020);
        assertNull(cache.get("key1"));
        assertNull(cache.get("key2"));

    }

    /**
     * Test expiry based on time to live.
     * <p/>
     * Elements are put quietly back into the cache after being cloned.
     * The elements should expire as if the putQuiet had not happened.
     */
    public void testExpiryBasedOnTimeToLiveAfterPutQuiet() throws Exception {
        //Set size so the second element overflows to disk.
        Cache cache = new Cache("test", 1, true, false, 5, 2);
        manager.addCache(cache);
        cache.put(new Element("key1", "value1"));
        cache.put(new Element("key2", "value1"));

        Element element1 = cache.get("key1");
        Element element2 = cache.get("key2");
        assertNotNull(element1);
        assertNotNull(element2);

        //Test time to live
        Thread.sleep(2020);
        //Should not affect age
        cache.putQuiet((Element) element2.clone());
        cache.putQuiet((Element) element2.clone());
        Thread.sleep(3020);
        assertNull(cache.get("key1"));
        assertNull(cache.get("key2"));
    }

    /**
     * Test expiry based on time to live
     */
    public void testNoIdleOrExpiryBasedOnTimeToLiveForEternal() throws Exception {
        //Set size so the second element overflows to disk.
        Cache cache = new Cache("test", 1, true, true, 5, 2);
        manager.addCache(cache);
        cache.put(new Element("key1", "value1"));
        cache.put(new Element("key2", "value1"));

        //Test time to live
        assertNotNull(cache.get("key1"));
        assertNotNull(cache.get("key2"));

        //Check that we did not idle out
        Thread.sleep(2020);
        assertNotNull(cache.get("key1"));
        assertNotNull(cache.get("key2"));

        //Check that we did not expire out
        Thread.sleep(3020);
        assertNotNull(cache.get("key1"));
        assertNotNull(cache.get("key2"));
    }

    /**
     * Test expiry based on time to idle.
     */
    public void testExpiryBasedOnTimeToIdle() throws Exception {
        //Set size so the second element overflows to disk.
        Cache cache = new Cache("test", 1, true, false, 6, 2);
        manager.addCache(cache);
        cache.put(new Element("key1", "value1"));
        cache.put(new Element("key2", "value1"));

        //Test time to idle
        Element element1 = cache.get("key1");
        Element element2 = cache.get("key2");
        assertNotNull(element1);
        assertNotNull(element2);
        Thread.sleep(2050);
        assertNull(cache.get("key1"));
        assertNull(cache.get("key2"));

        //Test effect of get
        cache.put(new Element("key1", "value1"));
        cache.put(new Element("key2", "value1"));
        Thread.sleep(1050);
        assertNotNull(cache.get("key1"));
        assertNotNull(cache.get("key2"));

        Thread.sleep(2050);
        assertNull(cache.get("key1"));
        assertNull(cache.get("key2"));
    }

    /**
     * Test expiry based on time to idle.
     */
    public void testExpiryBasedOnTimeToIdleAfterPutQuiet() throws Exception {
        //Set size so the second element overflows to disk.
        Cache cache = new Cache("test", 1, true, false, 5, 3);
        manager.addCache(cache);
        cache.put(new Element("key1", "value1"));
        cache.put(new Element("key2", "value1"));

        //Test time to idle
        Element element1 = cache.get("key1");
        Element element2 = cache.get("key2");
        assertNotNull(element1);
        assertNotNull(element2);

        //Now, getQuiet and check still times out 2 seconds after last get
        Thread.sleep(1050);
        element1 = cache.getQuiet("key1");
        element2 = cache.getQuiet("key2");
        Thread.sleep(2050);
        assertNull(cache.getQuiet("key1"));
        assertNull(cache.getQuiet("key2"));

        //Now put back in with putQuiet. Should be immediately expired
        cache.putQuiet((Element) element1.clone());
        cache.putQuiet((Element) element2.clone());
        assertNull(cache.get("key1"));
        element2 = cache.get("key2");
        assertNull(element2);
    }

    /**
     * Test element statistics, including get and getQuiet
     * eternal="false"
     * timeToIdleSeconds="5"
     * timeToLiveSeconds="10"
     * overflowToDisk="true"
     */
    public void testElementStatistics() throws Exception {
        //Set size so the second element overflows to disk.
        Cache cache = new Cache("test", 1, true, false, 5, 2);
        manager.addCache(cache);
        cache.put(new Element("key1", "value1"));
        cache.put(new Element("key2", "value1"));

        Element element1 = cache.get("key1");
        assertEquals("Should be one", 1, element1.getHitCount());
        element1 = cache.getQuiet("key1");
        assertEquals("Should be one", 1, element1.getHitCount());
        element1 = cache.get("key1");
        assertEquals("Should be two", 2, element1.getHitCount());
    }

    /**
     * Test cache statistics, including get and getQuiet
     */
    public void testCacheStatistics() throws Exception {
        //Set size so the second element overflows to disk.
        Cache cache = new Cache("test", 1, true, false, 5, 2);
        manager.addCache(cache);
        cache.put(new Element("key1", "value1"));
        cache.put(new Element("key2", "value1"));

        Element element1 = cache.get("key1");
        assertEquals("Should be one", 1, element1.getHitCount());
        assertEquals("Should be one", 1, cache.getHitCount());
        element1 = cache.getQuiet("key1");
        assertEquals("Should be one", 1, element1.getHitCount());
        assertEquals("Should be one", 1, cache.getHitCount());
        element1 = cache.get("key1");
        assertEquals("Should be two", 2, element1.getHitCount());
        assertEquals("Should be two", 2, cache.getHitCount());

        assertEquals("Should be 0", 0, cache.getMissCountNotFound());
        cache.get("doesnotexist");
        assertEquals("Should be 1", 1, cache.getMissCountNotFound());

    }

    /**
     * Checks that getQuiet works how we expect it to
     *
     * @throws Exception
     */
    public void testGetQuietAndPutQuiet() throws Exception {
        //Set size so the second element overflows to disk.
        Cache cache = new Cache("test", 1, true, false, 5, 2);
        manager.addCache(cache);
        cache.put(new Element("key1", "value1"));
        cache.put(new Element("key2", "value1"));

        Element element1 = cache.get("key1");
        long lastAccessedElement1 = element1.getLastAccessTime();
        long hitCountElement1 = element1.getHitCount();
        assertEquals("Should be two", 1, element1.getHitCount());
        assertEquals(1L, cache.getStatistics().getCacheHits());

        element1 = cache.getQuiet("key1");
        element1 = cache.getQuiet("key1");
        assertEquals(1L, cache.getStatistics().getCacheHits());
        Element clonedElement1 = (Element) element1.clone();
        cache.putQuiet(clonedElement1);
        element1 = cache.getQuiet("key1");
        assertEquals("last access time should be unchanged", lastAccessedElement1, element1.getLastAccessTime());
        assertEquals("hit count should be unchanged", hitCountElement1, element1.getHitCount());
        element1 = cache.get("key1");
        assertEquals("Should be two", 2, element1.getHitCount());
    }

    /**
     * Test size with put and remove.
     * <p/>
     * It checks that size makes sense, and also that getKeys.size() matches getSize()
     */
    public void testSizeWithPutAndRemove() throws Exception {
        //Set size so the second element overflows to disk.
        Cache cache = new Cache("test2", 1, true, true, 0, 0);
        manager.addCache(cache);
        cache.put(new Element("key1", "value1"));
        cache.put(new Element("key2", "value1"));
        int sizeFromGetSize = cache.getSize();
        int sizeFromKeys = cache.getKeys().size();
        assertEquals(sizeFromGetSize, sizeFromKeys);
        assertEquals(2, cache.getSize());
        cache.put(new Element("key1", "value1"));
        cache.put(new Element("key1", "value1"));

        //key1 should be in the Disk Store
        assertEquals(cache.getSize(), cache.getKeys().size());
        assertEquals(2, cache.getSize());
        //there were two of these, so size will now be one
        cache.remove("key1");
        assertEquals(cache.getSize(), cache.getKeys().size());
        assertEquals(1, cache.getSize());
        cache.remove("key2");
        assertEquals(cache.getSize(), cache.getKeys().size());
        assertEquals(0, cache.getSize());

        //try null values
        cache.put(new Element("nullValue1", null));
        cache.put(new Element("nullValue2", null));
        //Cannot overflow therefore just one
        assertEquals(1, cache.getSize());
        Element nullValueElement = cache.get("nullValue2");
        assertNull(nullValueElement.getValue());
        assertNull(nullValueElement.getObjectValue());

    }

    /**
     * Test getKeys after expiry
     * <p/>
     * Makes sure that if an element is expired, its key should also be expired
     */
    public void testGetKeysAfterExpiry() throws Exception {
        //Set size so the second element overflows to disk.
        Cache cache = new Cache("test2", 1, true, false, 1, 0);
        manager.addCache(cache);
        String key1 = "key1";
        cache.put(new Element(key1, "value1"));
        cache.put(new Element("key2", "value1"));
        //getSize uses getKeys().size(), so these should be the same
        assertEquals(cache.getSize(), cache.getKeys().size());
        //getKeys does not do an expiry check, so the expired elements are counted
        assertEquals(2, cache.getSize());
        String keyFromDisk = (String) cache.get(key1).getObjectKey();
        assertTrue(key1 == keyFromDisk);
        Thread.sleep(1050);
        assertEquals(2, cache.getKeys().size());
        //getKeysWithExpiryCheck does check and gives the correct answer of 0
        assertEquals(0, cache.getKeysWithExpiryCheck().size());
    }

    /**
     * Answers the question of whether key references are preserved as elements are written to disk.
     * This is not a mandatory part of the API. If this test breaks in future it should be removed.
     */
    public void testKeysEqualsEquals() throws Exception {
        //Set size so the second element overflows to disk.
        Cache cache = new Cache("test2", 0, true, false, 1, 0);
        manager.addCache(cache);
        String key1 = "key1";
        cache.put(new Element(key1, "value1"));
        cache.put(new Element("key2", "value1"));
        String keyFromDisk = (String) cache.get(key1).getObjectKey();
        assertTrue(key1 == keyFromDisk);
    }

    /**
     * Test size after multiple calls, with put and remove
     */
    public void testSizeMultipleCallsWithPutAndRemove() throws Exception {
        //Set size so the second element overflows to disk.
        Cache cache = new Cache("test3", 1, true, true, 0, 0);
        manager.addCache(cache);
        cache.put(new Element("key1", "value1"));
        cache.put(new Element("key2", "value1"));

        //key1 should be in the Disk Store
        assertEquals(2, cache.getSize());
        assertEquals(2, cache.getSize());
        assertEquals(2, cache.getSize());
        assertEquals(2, cache.getSize());
        assertEquals(2, cache.getSize());
        cache.remove("key1");
        assertEquals(1, cache.getSize());
        assertEquals(1, cache.getSize());
        assertEquals(1, cache.getSize());
        assertEquals(1, cache.getSize());
        assertEquals(1, cache.getSize());
        cache.remove("key2");
        assertEquals(0, cache.getSize());
        assertEquals(0, cache.getSize());
        assertEquals(0, cache.getSize());
        assertEquals(0, cache.getSize());
        assertEquals(0, cache.getSize());
    }

    /**
     * Checks the expense of checking for duplicates
     * Typical Results Duplicate Check: 8ms versus 3ms for No Duplicate Check
     * <p/>
     * 66ms for 1000, 6ms for no duplicate/expiry
     * 187565 for 100000, where 500 is the in-memory size. 964ms without checking expiry. 134ms for getKeysNoDuplicateCheckTime
     * 18795 for 100000, where 50000 is in-memory size. 873ms without checking expiry. 158ms for getKeysNoDuplicateCheckTime
     */
    public void testGetKeysPerformance() throws Exception {
        //Set size so the second element overflows to disk.
        Ehcache cache = createTestCache();

        for (int i = 0; i < 2000; i++) {
            cache.put(new Element("key" + i, "value"));
        }
        //let the notifiers cool down
        Thread.sleep(1000);
        StopWatch stopWatch = new StopWatch();
        List keys = cache.getKeys();
        assertTrue("Should be 2000 keys. ", keys.size() == 2000);
        long getKeysTime = stopWatch.getElapsedTime();
        cache.getKeysNoDuplicateCheck();
        long getKeysNoDuplicateCheckTime = stopWatch.getElapsedTime();
        LOG.info("Time to get 1000 keys: With Duplicate Check: " + getKeysTime + " Without Duplicate Check: "
                + getKeysNoDuplicateCheckTime);
        assertTrue("Getting keys took more than 150ms", getKeysTime < 100);
    }

    /**
     * Checks the expense of checking in-memory size
     * 3467890 bytes in 1601ms for JDK1.4.2
     */
    public void testCalculateInMemorySizePerformanceAndReasonableness() throws Exception {
        //Set size so the second element overflows to disk.
        Ehcache cache = createTestCache();

        //Set up object graphs
        for (int i = 0; i < 1000; i++) {
            HashMap map = new HashMap(100);
            for (int j = 0; j < 100; j++) {
                map.put("key" + j, new String[] { "adfdafs", "asdfdsafa", "sdfasdf" });
            }
            cache.put(new Element("key" + i, map));
        }

        StopWatch stopWatch = new StopWatch();
        long size = cache.calculateInMemorySize();
        assertTrue("Size is " + size + ". Check it for reasonableness.", size > 100000 && size < 5000000);
        long elapsed = stopWatch.getElapsedTime();
        LOG.info("In-memory size in bytes: " + size + " time to calculate in ms: " + elapsed);
        assertTrue("Calculate memory size takes less than 3.5 seconds", elapsed < 3500);
    }

    /**
     * Expire elements and verify size is correct.
     */
    public void testGetSizeAfterExpiry() throws Exception {
        //Set size so the second element overflows to disk.
        Cache cache = new Cache("test", 1, true, false, 1, 0);
        manager.addCache(cache);
        cache.put(new Element("key1", "value1"));
        cache.put(new Element("key2", "value1"));

        //Let the idle expire
        Thread.sleep(1020);
        assertEquals(null, cache.get("key1"));
        assertEquals(null, cache.get("key2"));

        assertEquals(0, cache.getSize());
    }

    /**
     * Test create and access times
     */
    public void testAccessTimes() throws Exception {
        //Set size so the second element overflows to disk.
        Cache cache = new Cache("test", 5, true, false, 5, 2);
        assertEquals(Status.STATUS_UNINITIALISED, cache.getStatus());
        manager.addCache(cache);
        Element newElement = new Element("key1", "value1");
        long creationTime = newElement.getCreationTime();
        assertTrue(newElement.getCreationTime() > (System.currentTimeMillis() - 500));
        assertTrue(newElement.getHitCount() == 0);
        assertTrue(newElement.getLastAccessTime() == 0);

        cache.put(newElement);

        Element element = cache.get("key1");
        assertNotNull(element);
        assertEquals(creationTime, element.getCreationTime());
        assertTrue(element.getLastAccessTime() != 0);
        assertTrue(element.getHitCount() == 1);

        //Check that access statistics were reset but not creation time
        cache.put(element);
        element = cache.get("key1");
        assertEquals(creationTime, element.getCreationTime());
        assertTrue(element.getLastAccessTime() != 0);
        assertTrue(element.getHitCount() == 1);
    }

    /**
     * Tests initialisation failures
     */
    public void testInitialiseFailures() {
        try {
            Cache cache = new Cache("testInitialiseFailures2", 1, false, false, 5, 1);
            cache.initialise();

            cache.initialise();
            fail("Should have thrown IllegalArgumentException");
        } catch (IllegalStateException e) {
            //noop
        }
    }

    /**
     * Tests putting nulls throws correct exception
     *
     * @throws Exception
     */
    public void testPutFailures() throws Exception {
        Cache cache = new Cache("testPutFailures", 1, false, false, 5, 1);
        manager.addCache(cache);

        try {
            cache.put(null);
            fail("Should have thrown IllegalArgumentException");
        } catch (IllegalArgumentException e) {
            //noop
        }

        try {
            cache.putQuiet(null);
            fail("Should have thrown IllegalArgumentException");
        } catch (IllegalArgumentException e) {
            //noop
        }

        //Null Elements like this are OK
        cache.putQuiet(new Element(null, null));
    }

    /**
     * Tests cache, memory store and disk store sizes from config
     */
    public void testSizes() throws Exception {
        Ehcache cache = getSampleCache1();

        assertEquals(0, cache.getMemoryStoreSize());

        for (int i = 0; i < 10010; i++) {
            cache.put(new Element("key" + i, "value1"));
        }
        assertEquals(10010, cache.getSize());
        assertEquals(10000, cache.getMemoryStoreSize());
        assertEquals(10, cache.getDiskStoreSize());

        //NonSerializable
        cache.put(new Element(new Object(), Object.class));

        assertEquals(10011, cache.getSize());
        assertEquals(10000, cache.getMemoryStoreSize());
        assertEquals(11, cache.getDiskStoreSize());

        cache.remove("key4");
        cache.remove("key3");

        assertEquals(10009, cache.getSize());
        assertEquals(10000, cache.getMemoryStoreSize());
        assertEquals(9, cache.getDiskStoreSize());

        cache.removeAll();
        assertEquals(0, cache.getSize());
        assertEquals(0, cache.getMemoryStoreSize());
        assertEquals(0, cache.getDiskStoreSize());

    }

    /**
     * Tests flushing the cache
     *
     * @throws Exception
     */
    public void testFlushWhenOverflowToDisk() throws Exception {
        Cache cache = new Cache("testFlushWhenOverflowToDisk", 50, true, false, 100, 200, true, 120);
        manager.addCache(cache);

        assertEquals(0, cache.getMemoryStoreSize());
        assertEquals(0, cache.getDiskStoreSize());

        for (int i = 0; i < 100; i++) {
            cache.put(new Element("" + i, new Date()));
        }
        //Not spoolable, should get ignored
        cache.put(new Element("key", new Object()));
        cache.put(new Element(new Object(), new Object()));
        cache.put(new Element(new Object(), "value"));

        //these "null" Elements are keyed the same way and only count as one
        cache.put(new Element(null, null));
        cache.put(new Element(null, null));

        cache.put(new Element("nullValue", null));

        assertEquals(50, cache.getMemoryStoreSize());
        assertEquals(55, cache.getDiskStoreSize());

        cache.flush();
        assertEquals(0, cache.getMemoryStoreSize());
        //Non Serializable Elements get discarded
        assertEquals(100, cache.getDiskStoreSize());

        cache.removeAll();

    }

    /**
     * When flushing large MemoryStores, OutOfMemory issues can happen if we are
     * not careful to move each to Element to the DiskStore, rather than copy them all
     * and then delete them from the MemoryStore.
     * <p/>
     * This test manipulates a MemoryStore right on the edge of what can fit into the 64MB standard VM size.
     * An inefficient spool will cause an OutOfMemoryException.
     *
     * @throws Exception
     */
    public void testMemoryEfficiencyOfFlushWhenOverflowToDisk() throws Exception {
        Cache cache = new Cache("testGetMemoryStoreSize", 40000, true, false, 100, 200, false, 120);
        manager.addCache(cache);
        StopWatch stopWatch = new StopWatch();

        assertEquals(0, cache.getMemoryStoreSize());

        for (int i = 0; i < 80000; i++) {
            cache.put(new Element("" + i, new byte[480]));
        }
        LOG.info("Put time: " + stopWatch.getElapsedTime());

        assertEquals(40000, cache.getMemoryStoreSize());
        assertEquals(40000, cache.getDiskStoreSize());

        long beforeMemory = measureMemoryUse();
        stopWatch.getElapsedTime();
        //todo
        //cache.flush();
        LOG.info("Flush time: " + stopWatch.getElapsedTime());

        //It takes a while to write all the Elements to disk
        Thread.sleep(1000);

        long afterMemory = measureMemoryUse();
        long memoryIncrease = afterMemory - beforeMemory;
        assertTrue(memoryIncrease < 40000000);

        assertEquals(40000, cache.getMemoryStoreSize());
        assertEquals(40000, cache.getDiskStoreSize());

    }

    /**
     * Shows the effect of jamming large amounts of puts into a cache that overflows to disk.
     * The DiskStore should cause puts to back off and avoid an out of memory error.
     */
    public void testBehaviourOnDiskStoreBackUp() throws Exception {
        Cache cache = new Cache("testGetMemoryStoreSize", 10, true, false, 100, 200, false, 0);
        manager.addCache(cache);

        assertEquals(0, cache.getMemoryStoreSize());

        Element a = null;
        int i = 0;
        try {
            for (; i < 200000; i++) {
                String key = i + "";
                String value = key;
                a = new Element(key,
                        value + "DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD");
                cache.put(a);
            }
        } catch (OutOfMemoryError e) {
            LOG.info("OutOfMemoryError: " + e.getMessage() + " " + i);
            fail();
        }
    }

    /**
     * Tests using elements with null values. They should work as normal.
     *
     * @throws Exception
     */
    public void testElementWithNullValue() throws Exception {
        Cache cache = new Cache("testElementWithNullValue", 10, false, false, 100, 200);
        manager.addCache(cache);

        Object key1 = new Object();
        Element element = new Element(key1, null);
        cache.put(element);
        assertNotNull(cache.get(key1));
        assertNotNull(cache.getQuiet(key1));
        assertSame(element, cache.get(key1));
        assertSame(element, cache.getQuiet(key1));
        assertNull(cache.get(key1).getObjectValue());
        assertNull(cache.getQuiet(key1).getObjectValue());

        assertEquals(false, cache.isExpired(element));
    }

    /**
     * Tests put works correctly for Elements with overriden TTL
     *
     * @throws Exception
     */
    public void testPutWithOverriddenTTLAndTTI() throws Exception {
        Cache cache = new Cache("testElementWithNullValue", 10, false, false, 1, 1);
        manager.addCache(cache);

        Object key = new Object();
        Element element = new Element(key, "value");
        element.setTimeToLive(2);
        cache.put(element);
        Thread.sleep(1050);
        assertNotNull(cache.get(key));
        assertSame(element, cache.get(key));

        Element element2 = new Element(key, "value");
        cache.put(element2);
        Thread.sleep(1050);
        assertNull(cache.get(key));

        Element element3 = new Element(key, "value");
        element3.setTimeToLive(2);
        cache.put(element3);
        Thread.sleep(1500);
        assertSame(element3, cache.get(key));

    }

    /**
     * Tests putQuiet works correctly for Elements with overriden TTL
     *
     * @throws Exception
     */
    public void testPutQuietWithOverriddenTTLAndTTI() throws Exception {
        Cache cache = new Cache("testElementWithNullValue", 10, false, false, 1, 1);
        manager.addCache(cache);

        Object key = new Object();
        Element element = new Element(key, "value");
        element.setTimeToLive(2);
        cache.putQuiet(element);
        Thread.sleep(1050);
        assertNotNull(cache.get(key));
        assertSame(element, cache.get(key));

        Element element2 = new Element(key, "value");
        cache.putQuiet(element2);
        Thread.sleep(1050);
        assertNull(cache.get(key));

        Element element3 = new Element(key, "value");
        element3.setTimeToLive(2);
        cache.putQuiet(element3);
        Thread.sleep(1500);
        assertSame(element3, cache.get(key));

    }

    /**
     * Tests using elements with null values. They should work as normal.
     *
     * @throws Exception
     */
    public void testNonSerializableElement() throws Exception {
        Cache cache = new Cache("testElementWithNonSerializableValue", 1, true, false, 100, 200);
        manager.addCache(cache);

        Element element1 = new Element("key1", new Object());
        Element element2 = new Element("key2", new Object());
        cache.put(element1);
        cache.put(element2);

        //Removed because could not overflow
        assertNull(cache.get("key1"));

        //Second one should be in the MemoryStore and retrievable
        assertNotNull(cache.get("key2"));
    }

    /**
     * Tests what happens when an Element throws an Error on serialization. This mimics
     * what a nasty error like OutOfMemoryError could do.
     * <p/>
     * Before a change to the SpoolAndExpiryThread to handle this situation this test failed and generated the following log message.
     * Jun 28, 2006 7:17:16 PM net.sf.ehcache.store.DiskStore put
     * SEVERE: testThreadKillerCache: Elements cannot be written to disk store because the spool thread has died.
     *
     * @throws Exception
     */
    public void testSpoolThreadHandlesThreadKiller() throws Exception {
        Cache cache = new Cache("testThreadKiller", 1, true, false, 100, 200);
        manager.addCache(cache);

        Element elementThreadKiller = new Element("key", new ThreadKiller());
        cache.put(elementThreadKiller);
        Element element1 = new Element("key1", "one");
        Element element2 = new Element("key2", "two");
        cache.put(element1);
        cache.put(element2);

        Thread.sleep(2000);

        assertNotNull(cache.get("key1"));
        assertNotNull(cache.get("key2"));
    }

    /**
     * Tests disk store and memory store size
     *
     * @throws Exception
     */
    public void testGetDiskStoreSize() throws Exception {
        Cache cache = new Cache("testGetDiskStoreSize", 1, true, false, 100, 200);
        manager.addCache(cache);
        assertEquals(0, cache.getDiskStoreSize());

        cache.put(new Element("key1", "value1"));
        assertEquals(0, cache.getDiskStoreSize());
        assertEquals(1, cache.getSize());

        cache.put(new Element("key2", "value2"));
        assertEquals(2, cache.getSize());
        assertEquals(1, cache.getDiskStoreSize());
        assertEquals(1, cache.getMemoryStoreSize());

        cache.put(new Element("key3", "value3"));
        cache.put(new Element("key4", "value4"));
        assertEquals(4, cache.getSize());
        assertEquals(3, cache.getDiskStoreSize());
        assertEquals(1, cache.getMemoryStoreSize());

        // remove last element inserted (is in memory store)
        assertNotNull(cache.getMemoryStore().get("key4"));
        cache.remove("key4");
        assertEquals(3, cache.getSize());
        assertEquals(3, cache.getDiskStoreSize());
        assertEquals(0, cache.getMemoryStoreSize());

        // remove key1 element
        assertNotNull(cache.getDiskStore().get("key1"));
        cache.remove("key1");
        assertEquals(2, cache.getSize());
        assertEquals(2, cache.getDiskStoreSize());
        assertEquals(0, cache.getMemoryStoreSize());

        // add another
        cache.put(new Element("key5", "value5"));
        assertEquals(3, cache.getSize());
        assertEquals(2, cache.getDiskStoreSize());
        assertEquals(1, cache.getMemoryStoreSize());

        // remove all
        cache.removeAll();
        assertEquals(0, cache.getSize());
        assertEquals(0, cache.getDiskStoreSize());
        assertEquals(0, cache.getMemoryStoreSize());

        //Check behaviour of NonSerializable objects
        cache.put(new Element(new Object(), new Object()));
        cache.put(new Element(new Object(), new Object()));
        cache.put(new Element(new Object(), new Object()));
        assertEquals(1, cache.getSize());
        assertEquals(0, cache.getDiskStoreSize());
        assertEquals(1, cache.getMemoryStoreSize());

    }

    /**
     * Tests that attempting to clone a cache fails with the right exception.
     *
     * @throws Exception
     */
    public void testCloneFailures() throws Exception {
        Cache cache = new Cache("testGetMemoryStore", 10, false, false, 100, 200);
        manager.addCache(cache);
        try {
            cache.clone();
            fail("Should have thrown CloneNotSupportedException");
        } catch (CloneNotSupportedException e) {
            //noop
        }
    }

    /**
     * Tests that the toString() method works.
     */
    public void testToString() {
        Ehcache cache = new Cache("testGetMemoryStore", 10, false, false, 100, 200);
        assertTrue(cache.toString().indexOf("testGetMemoryStore") > -1);
        assertEquals(410, cache.toString().length());
    }

    /**
     * When does equals mean the same thing as == ?
     *
     * @throws CacheException
     * @throws InterruptedException
     */
    public void testEquals() throws CacheException, InterruptedException {
        Cache cache = new Cache("cache", 1, true, false, 100, 200, false, 1);
        manager.addCache(cache);

        Element element1 = new Element("1", new Date());
        Element element2 = new Element("2", new Date());
        cache.put(element1);
        cache.put(element2);

        //Test equals and == from an Element retrieved from the MemoryStore
        Element elementFromStore = cache.get("2");
        assertEquals(element2, elementFromStore);
        assertTrue(element2 == elementFromStore);

        //Give the spool a chance to make sure it really got serialized to Disk
        Thread.sleep(300);

        //Test equals and == from an Element retrieved from the MemoryStore
        Element elementFromDiskStore = cache.get("1");
        assertEquals(element1, elementFromDiskStore);
        assertTrue(element1 != elementFromDiskStore);
    }

    /**
     * Tests the uniqueness of the GUID
     */
    public void testGuid() {
        Ehcache cache1 = new Cache("testGetMemoryStore", 10, false, false, 100, 200);
        Ehcache cache2 = new Cache("testGetMemoryStore", 10, false, false, 100, 200);
        String guid1 = cache1.getGuid();
        String guid2 = cache2.getGuid();
        assertEquals(cache1.getName(), cache2.getName());
        assertTrue(!guid1.equals(guid2));

    }

    /**
     * Does the Object API work?
     */
    public void testAPIObjectCompatibility() {
        Cache cache = new Cache("test", 5, true, false, 5, 2);
        manager.addCache(cache);

        Object objectKey = new Object();
        Object objectValue = new Object();
        Element objectElement = new Element(objectKey, objectValue);
        cache.put(objectElement);

        //Cannot get it back using get
        Element retrievedElement = cache.get(objectKey);
        assertNotNull(retrievedElement);
        try {
            retrievedElement.getObjectValue();
        } catch (CacheException e) {
            //expected
        }

        //Test that equals works
        retrievedElement = cache.get(objectKey);
        assertEquals(objectElement, retrievedElement);

        //Can with getObjectValue
        retrievedElement = cache.get(objectKey);
        assertEquals(objectValue, retrievedElement.getObjectValue());

    }

    /**
     * Does the Serializable API work?
     */
    public void testAPISerializableCompatibility() {
        Cache cache = new Cache("test", 5, true, false, 5, 2);
        manager.addCache(cache);

        //Try object compatibility
        Serializable key = new String();
        Element objectElement = new Element(key, new String());
        cache.put(objectElement);
        Object retrievedObject = cache.get(key);
        assertEquals(retrievedObject, objectElement);

        //Test that equals works
        assertEquals(objectElement, retrievedObject);
    }

    /**
     * Test issues reported.
     */
    public void testDiskStoreFlorian() {
        manager.shutdown();

        byte[] config = ("<ehcache> \n" + "<diskStore path=\"java.io.tmpdir\"/> \n" + "<defaultCache \n"
                + "            maxElementsInMemory=\"10000\" \n" + "            eternal=\"false\" \n"
                + "            timeToIdleSeconds=\"120\" \n" + "            timeToLiveSeconds=\"120\" \n"
                + "            overflowToDisk=\"true\" \n" + "            diskPersistent=\"false\" \n"
                + "            diskExpiryThreadIntervalSeconds=\"120\" \n"
                + "            memoryStoreEvictionPolicy=\"LRU\" \n" + "            /> " + "\n"
                + "<cache name=\"testCache\" \n" + "       maxElementsInMemory=\"20000\" \n"
                + "       eternal=\"false\" \n" + "       overflowToDisk=\"false\" \n"
                + "       timeToIdleSeconds=\"300\" \n" + "       timeToLiveSeconds=\"600\" \n"
                + "       diskPersistent=\"false\" \n" + "       diskExpiryThreadIntervalSeconds=\"1\" \n"
                + "       memoryStoreEvictionPolicy=\"LFU\" \n" + "/>           \n"
                + "<cache name=\"test2Cache\" \n" + "       maxElementsInMemory=\"20000\" \n"
                + "       eternal=\"false\" \n" + "       overflowToDisk=\"true\" \n"
                + "       timeToIdleSeconds=\"300\" \n" + "       timeToLiveSeconds=\"600\" \n"
                + "       diskPersistent=\"false\" \n" + "       diskExpiryThreadIntervalSeconds=\"1\" \n"
                + "       memoryStoreEvictionPolicy=\"LFU\" \n" + "/> \n" + "</ehcache> ").getBytes();

        CacheManager cacheManager = new CacheManager(new ByteArrayInputStream(config));
        Cache cache = new Cache("test3cache", 20000, true, false, 50, 30);
        assertTrue(cache.isOverflowToDisk());
        cacheManager.addCache(cache);

        for (int i = 0; i < 25000; i++) {
            cache.put(new Element(i + "", "value"));
        }

        assertEquals(5000, cache.getDiskStoreSize());
    }

    /**
     * Multi-thread read-write test with 20 threads
     * Just use MemoryStore to put max stress on cache
     * Values that work:
     * <pre>
     * size     threads     maxTime
     * 10000    50          200
     * 200000   50          500
     * 200000   500         800
     * </pre>
     */
    public void testReadWriteThreads() throws Exception {

        final int size = 10000;
        //set it higher for normal continuous integration so occasional higher numbes do not berak tests
        final int maxTime = (int) (500 * StopWatch.getSpeedAdjustmentFactor());
        manager.addCache(new Cache("test3cache", size, false, true, 30, 30));
        final Ehcache cache = manager.getEhcache("test3cache");

        CountingCacheLoader countingCacheLoader = new CountingCacheLoader();
        cache.setCacheLoader(countingCacheLoader);

        long start = System.currentTimeMillis();
        final List executables = new ArrayList();
        final Random random = new Random();

        for (int i = 0; i < size; i++) {
            cache.put(new Element("" + i, "value"));
        }

        //some of the time get data
        for (int i = 0; i < 26; i++) {
            final Executable executable = new Executable() {
                public void execute() throws Exception {
                    final StopWatch stopWatch = new StopWatch();
                    long start = stopWatch.getElapsedTime();
                    cache.get("key" + random.nextInt(size));
                    long end = stopWatch.getElapsedTime();
                    long elapsed = end - start;
                    assertTrue("Get time outside of allowed range: " + elapsed, elapsed < maxTime);
                }
            };
            executables.add(executable);
        }

        //some of the time add data
        for (int i = 0; i < 10; i++) {
            final Executable executable = new Executable() {
                public void execute() throws Exception {
                    final StopWatch stopWatch = new StopWatch();
                    long start = stopWatch.getElapsedTime();
                    cache.put(new Element("key" + random.nextInt(size), "value"));
                    long end = stopWatch.getElapsedTime();
                    long elapsed = end - start;
                    assertTrue("Put time outside of allowed range: " + elapsed, elapsed < maxTime);
                }
            };
            executables.add(executable);
        }

        //some of the time remove the data
        for (int i = 0; i < 10; i++) {
            final Executable executable = new Executable() {
                public void execute() throws Exception {
                    final StopWatch stopWatch = new StopWatch();
                    long start = stopWatch.getElapsedTime();
                    cache.remove("key" + random.nextInt(size));
                    long end = stopWatch.getElapsedTime();
                    long elapsed = end - start;
                    assertTrue("Remove time outside of allowed range: " + elapsed, elapsed < maxTime);
                }
            };
            executables.add(executable);
        }

        //some of the time remove the data
        for (int i = 0; i < 10; i++) {
            final Executable executable = new Executable() {
                public void execute() throws Exception {
                    final StopWatch stopWatch = new StopWatch();
                    long start = stopWatch.getElapsedTime();
                    cache.remove("key" + random.nextInt(size));
                    long end = stopWatch.getElapsedTime();
                    long elapsed = end - start;
                    assertTrue("Remove time outside of allowed range: " + elapsed, elapsed < maxTime);
                }
            };
            executables.add(executable);
        }

        //some of the time remove the data
        for (int i = 0; i < 10; i++) {
            final Executable executable = new Executable() {
                public void execute() throws Exception {
                    final StopWatch stopWatch = new StopWatch();
                    long start = stopWatch.getElapsedTime();
                    int randomInteger = random.nextInt(20);
                    if (randomInteger == 3) {
                        cache.removeAll();
                    }
                    long end = stopWatch.getElapsedTime();
                    long elapsed = end - start;
                    //remove all is slower
                    assertTrue("RemoveAll time outside of allowed range: " + elapsed, elapsed < (maxTime * 3));
                }
            };
            executables.add(executable);
        }

        //some of the time exercise the loaders through their various methods. Loader methods themselves make no performance
        //guarantees. They should only lock the cache when doing puts and gets, which the time limits on the other threads
        //will check for.
        for (int i = 0; i < 4; i++) {
            final Executable executable = new Executable() {
                public void execute() throws Exception {
                    int randomInteger = random.nextInt(20);
                    List keys = new ArrayList();
                    for (int i = 0; i < 2; i++) {
                        keys.add("key" + random.nextInt(size));
                    }
                    if (randomInteger == 1) {
                        cache.load("key" + random.nextInt(size));
                    } else if (randomInteger == 2) {
                        cache.loadAll(keys, null);
                    } else if (randomInteger == 3) {
                        cache.getWithLoader("key" + random.nextInt(size), null, null);
                    } else if (randomInteger == 4) {
                        cache.getAllWithLoader(keys, null);
                    }
                }
            };
            executables.add(executable);
        }

        runThreads(executables);
        long end = System.currentTimeMillis();
        LOG.info("Total time for the test: " + (end - start) + " ms");
        LOG.info("Total loads: " + countingCacheLoader.getLoadCounter());
        LOG.info("Total loadAlls: " + countingCacheLoader.getLoadAllCounter());
    }

    /**
     * Tests added from 1606323 Elements not stored in memory or on disk. This was supposedly
     * a bug but works.
     * This test passes.
     *
     * @throws Exception
     */
    public void testTimeToLive15552000() throws Exception {
        long timeToLiveSeconds = 15552000;
        doRunTest(timeToLiveSeconds);
    }

    /**
     * This test passes.
     *
     * @throws Exception
     */
    public void testTimeToLive604800() throws Exception {
        long timeToLiveSeconds = 604800;
        doRunTest(timeToLiveSeconds);
    }

    private void doRunTest(long timeToLiveSeconds) {
        String name = "memoryAndDiskCache";
        int maxElementsInMemory = 1000;
        MemoryStoreEvictionPolicy memoryStoreEvictionPolicy = MemoryStoreEvictionPolicy.LRU;
        boolean overflowToDisk = true;
        String diskStorePath = "java.io.tmp.dir/cache";
        boolean eternal = false;
        long timeToIdleSeconds = 0;
        boolean diskPersistent = true;
        long diskExpiryThreadIntervalSeconds = 3600;
        RegisteredEventListeners registeredEventListeners = null;
        BootstrapCacheLoader bootstrapCacheLoader = null;

        Cache memoryAndDisk = new Cache(name, maxElementsInMemory, memoryStoreEvictionPolicy, overflowToDisk,
                diskStorePath, eternal, timeToLiveSeconds, timeToIdleSeconds, diskPersistent,
                diskExpiryThreadIntervalSeconds, registeredEventListeners, bootstrapCacheLoader);

        this.manager.addCache(memoryAndDisk);

        String key = "test";
        Object value = new Object();

        memoryAndDisk.put(new Element(key, value));

        assertTrue(memoryAndDisk.isElementInMemory(key));
    }

    /**
     * Tests get from a finalize method, following a mailing list post from Felix Satyaputr
     *
     * @throws InterruptedException
     */
    public void testGetQuietFromFinalize() throws InterruptedException {

        final Cache cache = new Cache("test", 1, true, false, 5, 2);
        manager.addCache(cache);

        cache.put(new Element("key", "value"));
        cache.put(new Element("key2", "value"));
        cache.put(new Element("key3", "value"));
        cache.put(new Element("key4", "value"));
        cache.put(new Element("key5", "value"));

        //wait for overflow to kick in
        Thread.sleep(200);

        createTestObject();

        //try to get object finalized
        System.gc();
        Thread.sleep(200);
        System.gc();

    }

    private void createTestObject() {
        new TestObject();
    }

    /**
     * A class with a finalize implementation.
     */
    class TestObject {

        /**
         * Override the Object finalize method
         */
        protected void finalize() throws Throwable {
            manager.getCache("test").getQuiet("key");
            LOG.info("finalize run from thread " + Thread.currentThread().getName());
            super.finalize();
        }
    }

    public void testGetWithLoader() {

        Cache cache = manager.getCache("sampleCache1");
        cache.setCacheLoader(new CacheLoader() {
            public Object load(Object key, Object argument) throws CacheException {
                LOG.info("load1 " + key);
                return key;
            }

            /**
             * Load using both a key and an argument.
             * <p/>
             * JCache will use the loadAll(key) method where the argument is null.
             *
             * @param keys     the keys to load objects for
             * @param argument can be anything that makes sense to the loader
             * @return a map of Objects keyed by the collection of keys passed in.
             * @throws net.sf.jsr107cache.CacheException
             *
             */
            public Map loadAll(Collection keys, Object argument) throws net.sf.jsr107cache.CacheException {
                return null;
            }

            public String getName() {
                return null;
            }

            public Object load(Object o) throws CacheException {
                LOG.info("load2 " + o + " " + o.getClass());
                if (o.equals("c")) {
                    return null;
                }
                return o;
            }

            public Map loadAll(Collection collection) throws CacheException {
                return null;
            }
        });

        Element element = cache.get("a");
        assertNull(element);

        element = cache.getWithLoader("b", null, null);
        assertNotNull(element);

        element = cache.getWithLoader("c", null, null);
        assertNotNull(element);
    }

    /**
     * Tests the async load with a single item
     */
    public void testAsynchronousLoad() throws InterruptedException, ExecutionException {

        CountingCacheLoader countingCacheLoader = new CountingCacheLoader();
        Cache cache = manager.getCache("sampleCache1");
        cache.setCacheLoader(countingCacheLoader);
        ExecutorService executorService = cache.getExecutorService();

        Future future = cache.asynchronousLoad("key1", null, null);
        assertFalse(future.isDone());

        Object object = future.get();
        assertTrue(future.isDone());
        assertNull(object);

        assertFalse(executorService.isShutdown());

        assertEquals(1, cache.getSize());
        assertEquals(1, countingCacheLoader.getLoadCounter());
    }

    /**
     * Tests the async load with a single item
     */
    public void testGetWithLoaderException() {
        Cache cache = manager.getCache("sampleCache1");
        cache.setCacheLoader(new ExceptionThrowingLoader());
        try {
            cache.getWithLoader("key1", null, null);
            fail();
        } catch (CacheException e) {
            //expected
        }
    }

    /**
     * Tests the loadAll async method
     */
    public void testAsynchronousLoadAll() throws InterruptedException, ExecutionException {

        CountingCacheLoader countingCacheLoader = new CountingCacheLoader();
        Cache cache = manager.getCache("sampleCache1");
        cache.setCacheLoader(countingCacheLoader);
        ExecutorService executorService = cache.getExecutorService();

        List keys = new ArrayList();
        for (int i = 0; i < 1000; i++) {
            keys.add(new Integer(i));
        }

        Future future = cache.asynchronousLoadAll(keys, null);
        assertFalse(future.isDone());

        Object object = future.get();
        assertTrue(future.isDone());
        assertNull(object);

        assertFalse(executorService.isShutdown());

        assertEquals(1000, cache.getSize());
        assertEquals(1000, countingCacheLoader.getLoadAllCounter());
    }

    /**
     * Tests programmatically disabling and enabling a cache
     */
    public void testEnableAndDisable() throws Exception {
        Ehcache cache = manager.getCache("sampleCacheNoIdle");
        cache.put(new Element("key1put", "value1"));
        cache.put(new Element("key1putQuiet", "value1"));
        assertFalse(cache.isDisabled());
        assertNotNull(cache.get("key1put"));
        assertNotNull(cache.get("key1putQuiet"));

        //now disable
        cache.setDisabled(true);

        assertTrue(cache.isDisabled());
        assertNotNull(cache.get("key1put"));
        assertNotNull(cache.get("key1putQuiet"));

        cache.put(new Element("key2put", "value1"));
        cache.put(new Element("key2putQuiet", "value1"));
        assertNull(cache.get("key2put"));
        assertNull(cache.get("key2putQuiet"));
    }

}