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

com.sun.faces.util.LRUCache Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0, which is available at
 * http://www.eclipse.org/legal/epl-2.0.
 *
 * This Source Code may also be made available under the following Secondary
 * Licenses when the conditions for such availability set forth in the
 * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
 * version 2 with the GNU Classpath Exception, which is available at
 * https://www.gnu.org/software/classpath/license.html.
 *
 * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
 */
package com.sun.faces.util;

import java.util.Objects;
import java.util.concurrent.locks.ReentrantLock;

/**
 * LRU Cache adapted to the code style of Faces
 *
 * @author Paolo Bernardi
 */
public class LRUCache {

    // we can't use a read/write lock because getting an element
    // from a LinkedHashMap with access order creates internal
    // structural changes, so we have a unique lock.
    // We use the fair mode to better serve waiting threads, otherwise
    // we could simply use a synchronized block over an "Object lock"
    private final ReentrantLock lock = new ReentrantLock(true);

    // We use an LRUMap to reuse a common Faces' data structure.
    // this is backed by a LinkedHashMap with access order
    private final LRUMap cache;

    // we wrap the old Factory class of Faces' Cache
    // in a Function because it can be used natively with Maps
    private final Cache.Factory factory;

    public LRUCache(Cache.Factory factory,int capacity) {
        this.cache = new LRUMap<>(capacity);
        this.factory = factory;
    }

    /**
     * get from cache if exists
     * else init the value + save in cache + return created value
     *
     * @param key the key
     *
     * @return the value from cache if exists, otherwise the newly created and saved value
     */
    public V get(K key) {
        Objects.requireNonNull(key);

        // lock
        try {
            // if a waiting thread is interrupted it will release the lock... server shutdown?
            // if this introduces a performance penalty we could use lock.lock() and remove the try-catch
            lock.lockInterruptibly();
        }
        catch (InterruptedException e) {
            // in this case we return null
            // ...it should be ok, isn't it?
            return null;
        }

        // read + (create and store if not exist) atomically
        try {
            return cache.computeIfAbsent( key , factory );
        }
        finally {
            lock.unlock();
        }
    }

    /**
     * clear the cache
     */
    public void clear() {
        // lock
        try {
            // if a waiting thread is interrupted it will release the lock... server shutdown?
            // if this introduces a performance penalty we could use lock.lock() and remove the try-catch
            lock.lockInterruptibly();
        }
        // release if interrupted and return
        catch (InterruptedException ignored) {
            return;
        }
        // clear
        try {
            cache.clear();
        }
        // always unlock
        finally {
            lock.unlock();
        }
    }

    /**
     * remove an element identified by
     * the passed key from the cache
     */
    public V remove(final K key) {
        Objects.requireNonNull(key);

        // lock
        try {
            // if a waiting thread is interrupted it will release the lock... server shutdown?
            // if this introduces a performance penalty we could use lock.lock() and remove the try-catch
            lock.lockInterruptibly();
        }
        // release if interrupted and return
        catch (InterruptedException ignored) {
            return null;
        }
        // remove
        try {
            return cache.remove(key);
        }
        // always unlock
        finally {
            lock.unlock();
        }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy