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

com.enterprisemath.utils.cache.ExpiringInMemoryObjectCache Maven / Gradle / Ivy

package com.enterprisemath.utils.cache;

import com.enterprisemath.utils.DomainUtils;
import com.enterprisemath.utils.ValidationUtils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import org.apache.commons.lang3.builder.ToStringBuilder;

/**
 * Implementation of object cache which stores everything in memory and objects expires after
 * defined period of time.
 *
 * @author radek.hecl
 */
public class ExpiringInMemoryObjectCache implements ObjectCache {

    /**
     * Builder object.
     */
    public static class Builder {

        /**
         * Timeout for object expiration. In milliseconds.
         */
        private int expireTimeout = 0;

        /**
         * Whether timeout should be renewed when calling get method.
         */
        private Boolean renewOnGet = null;

        /**
         * Sets timeout for object expiration.
         *
         * @param expireTimeout timeout for object expiration, in milliseconds
         * @return this instance
         */
        public Builder setExpireTimeout(int expireTimeout) {
            this.expireTimeout = expireTimeout;
            return this;
        }

        /**
         * Sets whether timeout should be renewed when calling get method.
         *
         * @param renewOnGet whether timeout should be renewed when calling get method
         * @return this instance
         */
        public Builder setRenewOnGet(Boolean renewOnGet) {
            this.renewOnGet = renewOnGet;
            return this;
        }

        /**
         * Builds the result object.
         *
         * @return created object
         */
        public ExpiringInMemoryObjectCache build() {
            return new ExpiringInMemoryObjectCache(this);
        }
    }

    /**
     * Timeout for object expiration. In milliseconds.
     */
    private int expireTimeout;

    /**
     * Whether timeout should be renewed when calling get method.
     */
    private Boolean renewOnGet;

    /**
     * Buffer map.
     */
    private Map> buffer;

    /**
     * Object for locking.
     */
    private final Object lock = new Object();

    /**
     * Creates new instance.
     *
     * @param builder builder object
     */
    public ExpiringInMemoryObjectCache(Builder builder) {
        expireTimeout = builder.expireTimeout;
        renewOnGet = builder.renewOnGet;
        guardInvariants();
        buffer = new HashMap>();
    }

    /**
     * Guards this object to be consistent. Throws exception if this is not the case.
     */
    private void guardInvariants() {
        ValidationUtils.guardPositiveInt(expireTimeout, "expireTimeout must be positive");
        ValidationUtils.guardNotNull(renewOnGet, "renewOnGet cannot be null");
    }

    @Override
    public void put(String key, Object object) {
        synchronized (lock) {
            expireOld();
            Long et = System.currentTimeMillis() + expireTimeout;
            List pair = new ArrayList(2);
            pair.add(et);
            pair.add(object);
            buffer.put(key, pair);
        }
    }

    @Override
    public void remove(String key) {
        synchronized (lock) {
            expireOld();
            buffer.remove(key);
        }
    }

    @Override
    public Object get(String key) {
        synchronized (lock) {
            expireOld();
            if (!buffer.containsKey(key)) {
                throw new NoSuchElementException("element doesn't exists: key = " + key);
            }
            List pair = buffer.get(key);
            if (renewOnGet) {
                Long et = System.currentTimeMillis() + expireTimeout;
                pair.set(0, et);
            }
            return pair.get(1);
        }
    }

    @Override
    public Object get(String key, Object def) {
        synchronized (lock) {
            expireOld();
            if (!buffer.containsKey(key)) {
                return def;
            }
            List pair = buffer.get(key);
            if (renewOnGet) {
                Long et = System.currentTimeMillis() + expireTimeout;
                pair.set(0, et);
            }
            return pair.get(1);
        }
    }

    @Override
    public Set getKeys() {
        synchronized (lock) {
            expireOld();
            return DomainUtils.softCopySet(buffer.keySet());
        }
    }

    @Override
    public void clear() {
        synchronized (lock) {
            buffer = new HashMap>();
        }
    }

    /**
     * Expires old objects.
     */
    private void expireOld() {
        long ct = System.currentTimeMillis();
        Set remove = new HashSet();
        for (String key : buffer.keySet()) {
            List pair = buffer.get(key);
            if ((Long) pair.get(0) < ct) {
                remove.add(key);
            }
        }
        for (String key : remove) {
            buffer.remove(key);
        }
    }

    @Override
    public String toString() {
        return ToStringBuilder.reflectionToString(this);
    }

    /**
     * Creates new instance.
     *
     * @param expireTimeout timeout for object expiration, in milliseconds
     * @param renewOnGet whether timeout should be renewed on get
     * @return created instance
     */
    public static ExpiringInMemoryObjectCache create(int expireTimeout, boolean renewOnGet) {
        return new ExpiringInMemoryObjectCache.Builder().
                setExpireTimeout(expireTimeout).
                setRenewOnGet(renewOnGet).
                build();
    }
}