Package org.apache.jackrabbit.core.state

Source Code of org.apache.jackrabbit.core.state.MLRUItemStateCache$Entry

/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements.  See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.jackrabbit.core.state;

import org.apache.commons.collections.map.LRUMap;
import org.apache.commons.collections.map.LinkedMap;
import org.apache.jackrabbit.core.ItemId;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Set;
import java.util.Collections;
import java.util.Collection;
import java.util.ArrayList;
import java.util.Iterator;

/**
* An <code>ItemStateCache</code> implementation that internally uses a
* {@link LRUMap} to maintain a cache of <code>ItemState</code> objects. the
* cache uses a rough estimate of the memory consuption of the cache item
* states for calculating the maximum number of entries.
*/
public class MLRUItemStateCache implements ItemStateCache, Cache {
    /** Logger instance */
    private static Logger log = LoggerFactory.getLogger(LRUItemStateCache.class);

    /** default maximum memory to use */
    public static final int DEFAULT_MAX_MEM = 4 * 1024 * 1024;

    /** the amount of memory the entries use */
    private long totalMem;

    /** the maximum of memory the cache may use */
    private long maxMem;

    /** the number of writes */
    private long numWrites = 0;

    /** the access count */
    private long accessCount = 0;

    /** the cache access listeners */
    private CacheAccessListener accessListener;

    /**
     * A cache for <code>ItemState</code> instances
     */
    private final LinkedMap cache = new LinkedMap();

    /**
     * Constructs a new, empty <code>ItemStateCache</code> with a maximum amount
     * of memory of {@link #DEFAULT_MAX_MEM}.
     */
    public MLRUItemStateCache() {
        this(DEFAULT_MAX_MEM);
    }

    /**
     * Constructs a new, empty <code>ItemStateCache</code> with the specified
     * maximum memory.
     *
     * @param maxMem the maximum amount of memory this cache may use.
     */
    private MLRUItemStateCache(int maxMem) {
        this.maxMem = maxMem;
    }

    //-------------------------------------------------------< ItemStateCache >
    /**
     * {@inheritDoc}
     */
    public boolean isCached(ItemId id) {
        synchronized (cache) {
            return cache.containsKey(id);
        }
    }

    /**
     * {@inheritDoc}
     */
    public ItemState retrieve(ItemId id) {
        touch();
        synchronized (cache) {
            Entry entry = (Entry) cache.remove(id);
            if (entry != null) {
                // 'touch' item, by adding at end of list
                cache.put(id, entry);
                return entry.state;
            } else {
                return null;
            }
        }
    }

    /**
     * {@inheritDoc}
     */
    public void update(ItemId id) {
        touch();
        synchronized (cache) {
            Entry entry = (Entry) cache.get(id);
            if (entry != null) {
                totalMem -= entry.size;
                entry.recalc();
                totalMem += entry.size;
            }
        }
    }

    /**
     * {@inheritDoc}
     */
    public void cache(ItemState state) {
        touch();
        synchronized (cache) {
            ItemId id = state.getId();
            if (cache.containsKey(id)) {
                log.warn("overwriting cached entry " + id);
                evict(id);
            }
            Entry entry = new Entry(state);
            cache.put(id, entry);
            totalMem += entry.size;
            shrinkIfRequired();
            if (numWrites++%10000 == 0 && log.isDebugEnabled()) {
                log.info(this + " size=" + cache.size() + ", " + totalMem + "/" + maxMem);
            }
        }
    }

    private void shrinkIfRequired() {
        // remove items, if too many
        synchronized (cache) {
            while (totalMem > maxMem) {
                ItemId id = (ItemId) cache.firstKey();
                Entry entry = (Entry) cache.remove(id);
                totalMem -= entry.size;
            }
        }
    }

    /**
     * {@inheritDoc}
     */
    public void evict(ItemId id) {
        touch();
        synchronized (cache) {
            Entry entry = (Entry) cache.remove(id);
            if (entry != null) {
                totalMem -= entry.size;
            }
        }
    }

    /**
     * {@inheritDoc}
     */
    public void evictAll() {
        synchronized (cache) {
            cache.clear();
            totalMem = 0;
        }
    }

    /**
     * {@inheritDoc}
     */
    public boolean isEmpty() {
        synchronized (cache) {
            return cache.isEmpty();
        }
    }

    /**
     * {@inheritDoc}
     */
    public int size() {
        synchronized (cache) {
            return cache.size();
        }
    }

    /**
     * {@inheritDoc}
     */
    public Set keySet() {
        synchronized (cache) {
            return Collections.unmodifiableSet(cache.keySet());
        }
    }

    /**
     * {@inheritDoc}
     */
    public Collection values() {
        synchronized (cache) {
            ArrayList list = new ArrayList(cache.size());
            Iterator iter = cache.values().iterator();
            while (iter.hasNext()) {
                Entry entry = (Entry) iter.next();
                list.add(entry.state);
            }
            return list;
        }
    }

    private void touch() {
        accessCount++;
        if ((accessCount % CacheAccessListener.ACCESS_INTERVAL) == 0) {
            if (accessListener != null) {
                accessListener.cacheAccessed();
            }
        }
    }

    /**
     * {@inheritDoc}
     */
    public long getAccessCount() {
        return accessCount;
    }

    /**
     * {@inheritDoc}
     */
    public long getMaxMemorySize() {
        return maxMem;
    }

    /**
     * {@inheritDoc}
     */
    public long getMemoryUsed() {
        synchronized (cache) {
            totalMem = 0;
            Iterator iter = cache.values().iterator();
            while (iter.hasNext()) {
                Entry entry = (Entry) iter.next();
                entry.recalc();
                totalMem += entry.size;
            }
        }
        return totalMem;
    }

    /**
     * {@inheritDoc}
     */
    public void resetAccessCount() {
        synchronized (cache) {
            accessCount = 0;
        }
    }

    /**
     * {@inheritDoc}
     */
    public void setMaxMemorySize(long size) {
        synchronized (cache) {
            this.maxMem = size;
            shrinkIfRequired();
        }
    }

    /**
     * Set the cache access listener. Only one listener per cache is supported.
     *
     * @param listener the new listener
     */
    public void setAccessListener(CacheAccessListener listener) {
        this.accessListener = listener;
    }

    /**
     * {@inheritDoc}
     */
    public void dispose() {
        synchronized (cache) {
            if (accessListener != null) {
                accessListener.disposeCache(this);
            }
        }
    }


    /**
     * Internal cache entry.
     */
    private static class Entry {

        private final ItemState state;

        private long size;

        public Entry(ItemState state) {
            this.state = state;
            this.size = 64 + state.calculateMemoryFootprint();
        }

        public void recalc() {
            size = 64 + state.calculateMemoryFootprint();
        }
    }

}
TOP

Related Classes of org.apache.jackrabbit.core.state.MLRUItemStateCache$Entry

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.