This project has retired. For details please refer to its Attic page.
Source code
001package org.apache.archiva.components.cache.hashmap;
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 org.apache.archiva.components.cache.AbstractCache;
023import org.apache.archiva.components.cache.AbstractCacheStatistics;
024import org.apache.archiva.components.cache.Cache;
025import org.apache.archiva.components.cache.CacheStatistics;
026import org.apache.archiva.components.cache.CacheableWrapper;
027import org.slf4j.Logger;
028import org.slf4j.LoggerFactory;
029import org.springframework.stereotype.Service;
030
031import javax.annotation.PostConstruct;
032import java.util.Iterator;
033import java.util.LinkedHashMap;
034import java.util.Map;
035
036/**
037 * <p>
038 * HashMapCache - this is a Cache implementation taken from the Archiva project.
039 * </p>
040 * <p/>
041 * <p>
042 * Original class written by Edwin Punzalan for purposes of addressing the
043 * jira ticket <a href="http://jira.codehaus.org/browse/MRM-39">MRM-39</a>
044 * </p>
045 * <p>
046 * Configure the refreshTime in seconds value configure a ttl of object life in cache.
047 * Object get( Object key ) :
048 * <ul>
049 * <li> &lt; 0 : method will always return null (no cache)</li>
050 * <li> = 0 : first stored object will be return (infinite life in the cache)</li>
051 * <li> > 0 : after a live (stored time) of refreshTime the object will be remove from the cache
052 * and a no object will be returned by the method</li>
053 * </ul>
054 * </p>
055 *
056 * @author Edwin Punzalan
057 * @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a>
058 */
059@Service( "cache#hashmap" )
060public class HashMapCache<V, T>
061    extends AbstractCache<V, T>
062    implements Cache<V, T>
063{
064
065    private Logger log = LoggerFactory.getLogger( getClass( ) );
066
067    class Stats
068        extends AbstractCacheStatistics
069        implements CacheStatistics
070    {
071
072        public Stats( )
073        {
074            super( );
075        }
076
077        public long getSize( )
078        {
079            synchronized (cache)
080            {
081                return cache.size( );
082            }
083        }
084
085    }
086
087    private Map<V, CacheableWrapper<T>> cache;
088
089    /**
090     *
091     */
092    private double cacheHitRatio = 1.0;
093
094    /**
095     *
096     */
097    private int cacheMaxSize = 0;
098
099    /**
100     *
101     */
102    private int refreshTime;
103
104    private Stats stats;
105
106    public HashMapCache( )
107    {
108        // noop
109    }
110
111    /**
112     * Empty the cache and reset the cache hit rate
113     */
114    public void clear( )
115    {
116        synchronized (cache)
117        {
118            stats.clear( );
119            cache.clear( );
120        }
121    }
122
123    /**
124     * Check for a cached object and return it if it exists. Returns null when the keyed object is not found
125     *
126     * @param key the key used to map the cached object
127     * @return the object mapped to the given key, or null if no cache object is mapped to the given key
128     */
129    public T get( V key )
130    {
131        CacheableWrapper<T> retValue = null;
132        // prevent search
133        if ( !this.isCacheAvailable( ) )
134        {
135            return null;
136        }
137        synchronized (cache)
138        {
139            if ( cache.containsKey( key ) )
140            {
141                // remove and put: this promotes it to the top since we use a linked hash map
142                retValue = cache.remove( key );
143
144                if ( needRefresh( retValue ) )
145                {
146                    stats.miss( );
147                    return null;
148                }
149                else
150                {
151                    cache.put( key, retValue );
152                    stats.hit( );
153                }
154            }
155            else
156            {
157                stats.miss( );
158            }
159        }
160
161        return retValue == null ? null : retValue.getValue( );
162    }
163
164
165    protected boolean needRefresh( CacheableWrapper cacheableWrapper )
166    {
167        if ( cacheableWrapper == null )
168        {
169            return true;
170        }
171        if ( this.getRefreshTime( ) == 0 )
172        {
173            return false;
174        }
175        boolean result =
176            ( System.currentTimeMillis( ) - cacheableWrapper.getStoredTime( ) ) > ( this.getRefreshTime( ) * 1000 );
177
178        log.debug( "{} is uptodate {}", cacheableWrapper, result );
179
180        return result;
181    }
182
183    public CacheStatistics getStatistics( )
184    {
185        return stats;
186    }
187
188    /**
189     * Check if the specified key is already mapped to an object.
190     *
191     * @param key the key used to map the cached object
192     * @return true if the cache contains an object associated with the given key
193     */
194    public boolean hasKey( V key )
195    {
196        // prevent search
197        if ( !this.isCacheAvailable( ) )
198        {
199            return false;
200        }
201        boolean contains;
202        synchronized (cache)
203        {
204            contains = cache.containsKey( key );
205
206            if ( contains )
207            {
208                stats.hit( );
209            }
210            else
211            {
212                stats.miss( );
213            }
214        }
215
216        return contains;
217    }
218
219    @PostConstruct
220    public void initialize( )
221    {
222        stats = new Stats( );
223
224        if ( cacheMaxSize > 0 )
225        {
226            cache = new LinkedHashMap<>( cacheMaxSize );
227        }
228        else
229        {
230            cache = new LinkedHashMap<>( );
231        }
232    }
233
234    /**
235     * Cache the given value and map it using the given key
236     *
237     * @param key   the object to map the valued object
238     * @param value the object to cache
239     */
240    public T put( V key, T value )
241    {
242        CacheableWrapper<T> ret = null;
243
244        // remove and put: this promotes it to the top since we use a linked hash map
245        synchronized (cache)
246        {
247            if ( cache.containsKey( key ) )
248            {
249                cache.remove( key );
250            }
251
252            ret = cache.put( key, new CacheableWrapper<>( value, System.currentTimeMillis( ) ) );
253        }
254
255        manageCache( );
256
257        return ret == null ? null : ret.getValue( );
258    }
259
260    /**
261     * Cache the given value and map it using the given key
262     *
263     * @param key   the object to map the valued object
264     * @param value the object to cache
265     */
266    public void register( V key, T value )
267    {
268        // remove and put: this promotes it to the top since we use a linked hash map
269        synchronized (cache)
270        {
271            if ( cache.containsKey( key ) )
272            {
273                cache.remove( key );
274            }
275
276            cache.put( key, new CacheableWrapper<>( value, System.currentTimeMillis( ) ) );
277        }
278
279        manageCache( );
280    }
281
282    public T remove( V key )
283    {
284        synchronized (cache)
285        {
286            if ( cache.containsKey( key ) )
287            {
288                return cache.remove( key ).getValue( );
289            }
290        }
291
292        return null;
293    }
294
295    private void manageCache( )
296    {
297        synchronized (cache)
298        {
299            Iterator iterator = cache.entrySet( ).iterator( );
300            if ( cacheMaxSize == 0 )
301            {
302                // desired HitRatio is reached, we can trim the cache to conserve memory
303                if ( cacheHitRatio <= stats.getCacheHitRate( ) )
304                {
305                    iterator.next( );
306                    iterator.remove( );
307                }
308            }
309            else if ( cache.size( ) > cacheMaxSize )
310            {
311                // maximum cache size is reached
312                while ( cache.size( ) > cacheMaxSize )
313                {
314                    iterator.next( );
315                    iterator.remove( );
316                }
317            }
318            else
319            {
320                // even though the max has not been reached, the desired HitRatio is already reached,
321                // so we can trim the cache to conserve memory
322                if ( cacheHitRatio <= stats.getCacheHitRate( ) )
323                {
324                    iterator.next( );
325                    iterator.remove( );
326                }
327            }
328        }
329    }
330
331
332    public int getRefreshTime( )
333    {
334        return refreshTime;
335    }
336
337    /**
338     *
339     */
340    public void setRefreshTime( int refreshTime )
341    {
342        this.refreshTime = refreshTime;
343    }
344
345    /**
346     * @return true, if the cache is available, otherwise false
347     */
348    protected boolean isCacheAvailable( )
349    {
350        return this.getRefreshTime( ) >= 0;
351    }
352
353    public double getCacheHitRatio( )
354    {
355        return cacheHitRatio;
356    }
357
358    public void setCacheHitRatio( double cacheHitRatio )
359    {
360        this.cacheHitRatio = cacheHitRatio;
361    }
362
363    public int getCacheMaxSize( )
364    {
365        return cacheMaxSize;
366    }
367
368    public void setCacheMaxSize( int cacheMaxSize )
369    {
370        this.cacheMaxSize = cacheMaxSize;
371    }
372
373    public Stats getStats( )
374    {
375        return stats;
376    }
377}