dk.statsbiblioteket.util.caching.PendingCache.java Source code

Java tutorial

Introduction

Here is the source code for dk.statsbiblioteket.util.caching.PendingCache.java

Source

/* $Id: $
 *
 * The SB Util Library.
 * Copyright (C) 2005-2007  The State and University Library of Denmark
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */
package dk.statsbiblioteket.util.caching;

import dk.statsbiblioteket.util.qa.QAInfo;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.util.*;

/**
 * Encapsulation of {@link TimeSensitiveCache} that uses {@link PendingElement}s
 * and exposes the wait-for-value principle.
 *
 * Note that the methods {@link #values} and {@link #entrySet} only returns
 * assigned values.
 */
@QAInfo(level = QAInfo.Level.NORMAL, state = QAInfo.State.IN_DEVELOPMENT, author = "te")
public class PendingCache<V, T> implements Map<V, T> {
    private static Log log = LogFactory.getLog(PendingCache.class);

    private final TimeSensitiveCache<V, PendingElement<T>> inner;

    /**
     * Construct a new TimeSensitiveCache.
     *
     * @param timeToLive  Time that the elements will live in the cache
     * @param accessOrder if true, the elements have their timestamp refreshed
     *                    when "gotten". Otherwise, they will be removed in insertion order
     * @param fixedSize   the fixed size of the cache. When elements are inserted
     *                    above this limit, the oldest element in the cache is removed, even if it
     *                    was not to old yet
     */
    public PendingCache(long timeToLive, boolean accessOrder, int fixedSize) {
        inner = new TimeSensitiveCache<V, PendingElement<T>>(timeToLive, accessOrder, fixedSize);
    }

    /**
     * Construct a new TimeSensitiveCache, without a fixed size.
     *
     * @param timeToLive  Time that the elements will live in the cache
     * @param accessOrder if true, the elements have their timestamp refreshed
     *                    when "gotten". Otherwise, they will be removed in insertion order
     */
    public PendingCache(long timeToLive, boolean accessOrder) {
        inner = new TimeSensitiveCache<V, PendingElement<T>>(timeToLive, accessOrder);
    }

    /**
     * If an element exists for the given key it is either assigned or not
     * assigned. If it is assigned, the inner value will be returned
     * immediately. If it is not assigned, the method will block until a
     * value has been assigned.
     *
     * @param key the key for the wanted value.
     * @return null if there was either no value for the given key or null was
     *         the value for the given key.
     */
    @Override
    public T get(Object key) {
        PendingElement<T> element = inner.get(key);
        if (element == null) {
            return null;
        }
        return element.getValue();
    }

    /**
     * If an element exists for the given key it is either assigned or not
     * assigned. If it is assigned, the inner value will be returned
     * immediately. If it is not assigned, the method will block until a
     * value has been assigned or until the given timeout is reached.
     *
     * @param key the key for the wanted value.
     * @param ms  the maximum number of milliseconds to wait for a value to
     *            be assigned if the element exists.
     * @return the value for the key or null if there was either no value for
     *         the given key or null was the value for the given key or the timeout was
     *         reached..
     */
    public T get(V key, long ms) {
        PendingElement<T> element = inner.get(key);
        if (element == null) {
            return null;
        }
        return element.getValue(ms);
    }

    @Override
    public boolean containsValue(Object value) {
        for (Entry<V, PendingElement<T>> entry : inner.entrySet()) {
            if (entry.getValue().isAssigned() && ((entry.getValue() == null && value == null)
                    || (entry.getValue() != null && entry.getValue().equals(value)))) {
                return true;
            }
        }
        return false;
    }

    /**
     * Puts the value directly into the map. This is a standard map operation
     * with no wait-for-value functionality.
     *
     * @param key   standard map key.
     * @param value standard map value.
     * @return the old value if one was assigned (non-blocking).
     * @see #putPending
     */
    @Override
    public synchronized T put(V key, T value) {
        PendingElement<T> old = inner.put(key, new PendingElement<T>(value));
        if (old != null && old.isAssigned()) {
            return old.getValue();
        }
        return null;
    }

    /**
     * Creates a placeholder in the map, Other threads requesting the value for
     * the key will block until the value has been assigned to the placeholder.
     *
     * @param key standard map key.
     * @return a pending element that waits for a value to be assigned.
     */
    public synchronized PendingElement<T> putPending(V key) {
        PendingElement<T> pending = new PendingElement<T>();
        inner.put(key, pending);
        return pending;
    }

    @Override
    public Collection<T> values() {
        List<T> result = new ArrayList<T>(size());
        for (Entry<V, PendingElement<T>> entry : inner.entrySet()) {
            if (entry.getValue().isAssigned() && entry.getValue() != null) {
                result.add(entry.getValue().getValue());
            }
        }
        return result;
    }

    @Override
    public Set<Entry<V, T>> entrySet() {
        Set<Entry<V, T>> result = new HashSet<Entry<V, T>>(size());
        for (Entry<V, PendingElement<T>> entry : inner.entrySet()) {
            if (entry.getValue().isAssigned() && entry.getValue() != null) {
                final Entry<V, PendingElement<T>> fEntry = entry;
                Entry<V, T> newEntry = new Entry<V, T>() {
                    private V key = fEntry.getKey();
                    private T value = fEntry.getValue().getValue();

                    @Override
                    public V getKey() {
                        return key;
                    }

                    @Override
                    public T getValue() {
                        return value;
                    }

                    @Override
                    public T setValue(T value) {
                        T oldValue = this.value;
                        this.value = value;
                        return oldValue;
                    }
                };
                result.add(newEntry);
            }
        }
        return result;
    }

    @Override
    public void putAll(Map<? extends V, ? extends T> m) {
        for (Entry<? extends V, ? extends T> entry : m.entrySet()) {
            put(entry.getKey(), entry.getValue());
        }
    }

    @Override
    public T remove(Object key) {
        PendingElement<T> pending = inner.remove(key);
        if (pending == null || !pending.isAssigned()) {
            return null;
        }
        return pending.getValue();
    }

    /* Plain delegates */

    @Override
    public void clear() {
        inner.clear();
    }

    @Override
    public Set<V> keySet() {
        return inner.keySet();
    }

    @Override
    public int size() {
        return inner.size();
    }

    @Override
    public boolean isEmpty() {
        return inner.isEmpty();
    }

    @Override
    public boolean containsKey(Object key) {
        return inner.containsKey(key);
    }

}