001package org.apache.commons.jcs3.engine.memory.lru;
002
003/*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 *
012 *   http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied.  See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022import java.io.IOException;
023import java.util.Collections;
024import java.util.Map;
025
026import org.apache.commons.jcs3.engine.behavior.ICacheElement;
027import org.apache.commons.jcs3.engine.control.CompositeCache;
028import org.apache.commons.jcs3.engine.memory.AbstractMemoryCache;
029import org.apache.commons.jcs3.engine.memory.util.MemoryElementDescriptor;
030import org.apache.commons.jcs3.engine.stats.behavior.IStats;
031import org.apache.commons.jcs3.log.Log;
032import org.apache.commons.jcs3.log.LogManager;
033
034/**
035 * This is a test memory manager using the jdk1.4 LinkedHashMap.
036 */
037public class LHMLRUMemoryCache<K, V>
038    extends AbstractMemoryCache<K, V>
039{
040    /** The Logger. */
041    private static final Log log = LogManager.getLog( LRUMemoryCache.class );
042
043    /**
044     * For post reflection creation initialization
045     * <p>
046     * @param hub
047     */
048    @Override
049    public void initialize( final CompositeCache<K, V> hub )
050    {
051        super.initialize( hub );
052        log.info( "initialized LHMLRUMemoryCache for {0}", this::getCacheName );
053    }
054
055    /**
056     * Returns a synchronized LHMSpooler
057     * <p>
058     * @return Collections.synchronizedMap( new LHMSpooler() )
059     */
060    @Override
061    public Map<K, MemoryElementDescriptor<K, V>> createMap()
062    {
063        return Collections.synchronizedMap( new LHMSpooler() );
064    }
065
066    /**
067     * Puts an item to the cache.
068     * <p>
069     * @param ce Description of the Parameter
070     * @throws IOException
071     */
072    @Override
073    public void update( final ICacheElement<K, V> ce )
074        throws IOException
075    {
076        putCnt.incrementAndGet();
077        map.put( ce.getKey(), new MemoryElementDescriptor<>(ce) );
078    }
079
080    /**
081     * Update control structures after get
082     * (guarded by the lock)
083     *
084     * @param me the memory element descriptor
085     */
086    @Override
087    protected void lockedGetElement(final MemoryElementDescriptor<K, V> me)
088    {
089        // empty
090    }
091
092    /**
093     * Remove element from control structure
094     * (guarded by the lock)
095     *
096     * @param me the memory element descriptor
097     */
098    @Override
099    protected void lockedRemoveElement(final MemoryElementDescriptor<K, V> me)
100    {
101        // empty
102    }
103
104    /**
105     * Removes all cached items from the cache control structures.
106     * (guarded by the lock)
107     */
108    @Override
109    protected void lockedRemoveAll()
110    {
111        // empty
112    }
113
114    /**
115     * This returns semi-structured information on the memory cache, such as the size, put count,
116     * hit count, and miss count.
117     * <p>
118     * @return IStats
119     */
120    @Override
121    public IStats getStatistics()
122    {
123        final IStats stats = super.getStatistics();
124        stats.setTypeName( "LHMLRU Memory Cache" );
125
126        return stats;
127    }
128
129    // ---------------------------------------------------------- debug methods
130
131    /**
132     * Dump the cache entries from first to last for debugging.
133     */
134    public void dumpCacheEntries()
135    {
136        dumpMap();
137    }
138
139    /**
140     * This can't be implemented.
141     * <p>
142     * @param numberToFree
143     * @return 0
144     * @throws IOException
145     */
146    @Override
147    public int freeElements( final int numberToFree )
148        throws IOException
149    {
150        // can't be implemented using the LHM
151        return 0;
152    }
153
154    // ---------------------------------------------------------- extended map
155
156    /**
157     * Implementation of removeEldestEntry in LinkedHashMap
158     */
159    protected class LHMSpooler
160        extends java.util.LinkedHashMap<K, MemoryElementDescriptor<K, V>>
161    {
162        /** Don't change. */
163        private static final long serialVersionUID = -1255907868906762484L;
164
165        /**
166         * Initialize to a small size--for now, 1/2 of max 3rd variable "true" indicates that it
167         * should be access and not time governed. This could be configurable.
168         */
169        public LHMSpooler()
170        {
171            super( (int) ( getCacheAttributes().getMaxObjects() * .5 ), .75F, true );
172        }
173
174        /**
175         * Remove eldest. Automatically called by LinkedHashMap.
176         * <p>
177         * @param eldest
178         * @return true if removed
179         */
180        @SuppressWarnings("synthetic-access")
181        @Override
182        protected boolean removeEldestEntry( final Map.Entry<K, MemoryElementDescriptor<K, V>> eldest )
183        {
184            final ICacheElement<K, V> element = eldest.getValue().getCacheElement();
185
186            if ( size() <= getCacheAttributes().getMaxObjects() )
187            {
188                return false;
189            }
190            log.debug( "LHMLRU max size: {0}. Spooling element, key: {1}",
191                    () -> getCacheAttributes().getMaxObjects(), element::getKey);
192
193            waterfal( element );
194
195            log.debug( "LHMLRU size: {0}", () -> map.size() );
196            return true;
197        }
198    }
199}