org.bubblecloud.ilves.cache.InMemoryCache.java Source code

Java tutorial

Introduction

Here is the source code for org.bubblecloud.ilves.cache.InMemoryCache.java

Source

/**
 * Copyright 2013 Tommi S.E. Laukkanen
 *
 * 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 org.bubblecloud.ilves.cache;

import org.apache.commons.collections.MapIterator;
import org.apache.commons.collections.map.LRUMap;

import java.util.LinkedList;

/**
 * Simple in memory cache.
 *
 * @author Tommi S.E. Laukkanen
 */

public class InMemoryCache<K, T> {
    /**
     * The time to live for all cached object in ms.
     */
    private final long timeToLiveMillis;
    /**
     * The internal map containing cached objects.
     */
    private final LRUMap cacheMap;

    /**
     * Constructor defining time to live, cache evict expired intervals and maximum cached items.
     *
     * @param timeToLiveMillis      the time to live in milliseconds
     * @param evictIntervalMillis the clean up interval in milliseconds
     * @param maxItems              the maximum number of cached items.
     */
    public InMemoryCache(final long timeToLiveMillis, final long evictIntervalMillis, final int maxItems) {
        this.timeToLiveMillis = timeToLiveMillis;

        cacheMap = new LRUMap(maxItems);

        if (this.timeToLiveMillis > 0 && evictIntervalMillis > 0) {

            final Thread evictThread = new Thread(new Runnable() {
                public void run() {
                    while (true) {
                        try {
                            Thread.sleep(evictIntervalMillis);
                        } catch (final InterruptedException e) {
                        }
                        evictExpired();
                    }
                }
            });

            evictThread.setDaemon(true);
            evictThread.start();
        }
    }

    /**
     * Puts object in cache.
     *
     * @param key   the key
     * @param value the value
     */
    public void put(K key, T value) {
        synchronized (cacheMap) {
            cacheMap.put(key, new CacheObject(value));
        }
    }

    /**
     * Gets object from cache.
     *
     * @param key the key
     * @return the cached object or null.
     */
    public T get(K key) {
        synchronized (cacheMap) {
            CacheObject c = (CacheObject) cacheMap.get(key);

            if (c == null)
                return null;
            else {
                c.lastAccessed = System.currentTimeMillis();
                return c.value;
            }
        }
    }

    /**
     * Removes object from cache.
     *
     * @param key the key
     */
    public void remove(K key) {
        synchronized (cacheMap) {
            cacheMap.remove(key);
        }
    }

    /**
     * Gets cache size.
     *
     * @return the size
     */
    public int size() {
        synchronized (cacheMap) {
            return cacheMap.size();
        }
    }

    /**
     * Evicts expired objects from cache.
     */
    public void evictExpired() {
        final long now = System.currentTimeMillis();
        final LinkedList<K> expiredKeys = new LinkedList<K>();

        synchronized (cacheMap) {
            final MapIterator itr = cacheMap.mapIterator();
            while (itr.hasNext()) {
                final K key = (K) itr.next();
                final CacheObject cacheObject = (CacheObject) itr.getValue();

                if (cacheObject != null && (now > (timeToLiveMillis + cacheObject.lastAccessed))) {
                    expiredKeys.add(key);
                }
            }
        }

        for (K key : expiredKeys) {
            synchronized (cacheMap) {
                cacheMap.remove(key);
            }
        }
    }

    /**
     * Checks whether cache contains given key.
     * @param key the key
     * @return true if key is contained in the cache
     */
    public boolean containsKey(K key) {
        synchronized (cacheMap) {
            return cacheMap.containsKey(key);
        }
    }

    /**
     * The cache object containing lastAccess and value.
     */
    private class CacheObject {
        /**
         * The last access time.
         */
        public long lastAccessed = System.currentTimeMillis();
        /**
         * The cached value.
         */
        public final T value;

        /**
         * Constructor which sets the cached value.
         * @param value the cached value
         */
        private CacheObject(final T value) {
            this.value = value;
        }

    }

}