001    package org.apache.jcs.access;
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    
022    import java.io.IOException;
023    import java.io.Serializable;
024    import java.util.HashMap;
025    import java.util.Map;
026    import java.util.Set;
027    
028    import org.apache.commons.logging.Log;
029    import org.apache.commons.logging.LogFactory;
030    import org.apache.jcs.access.behavior.ICacheAccess;
031    import org.apache.jcs.access.exception.CacheException;
032    import org.apache.jcs.access.exception.InvalidArgumentException;
033    import org.apache.jcs.access.exception.InvalidHandleException;
034    import org.apache.jcs.access.exception.ObjectExistsException;
035    import org.apache.jcs.engine.CacheElement;
036    import org.apache.jcs.engine.behavior.ICacheElement;
037    import org.apache.jcs.engine.behavior.ICompositeCacheAttributes;
038    import org.apache.jcs.engine.behavior.IElementAttributes;
039    import org.apache.jcs.engine.control.CompositeCache;
040    import org.apache.jcs.engine.control.CompositeCacheManager;
041    import org.apache.jcs.engine.stats.behavior.ICacheStats;
042    
043    /**
044     * This class provides an interface for all types of access to the cache.
045     * <p>
046     * An instance of this class is tied to a specific cache region. Static methods are provided to get
047     * such instances.
048     * <p>
049     * Using this class you can retrieve an item, the item's wrapper, and the element's configuration.  You can also put an
050     * item in the cache, remove an item, and clear a region.
051     * <p>
052     * The JCS class is the preferred way to access these methods.
053     */
054    public class CacheAccess<K extends Serializable, V extends Serializable>
055        implements ICacheAccess<K, V>
056    {
057        /** The logger. */
058        private static final Log log = LogFactory.getLog( CacheAccess.class );
059    
060        /** Cache manager use by the various forms of defineRegion and getAccess */
061        private static CompositeCacheManager cacheMgr;
062    
063        /**
064         * The cache that a given instance of this class provides access to.
065         * <p>
066         * @TODO Should this be the interface?
067         */
068        protected CompositeCache<K, V> cacheControl;
069    
070        /**
071         * Constructor for the CacheAccess object.
072         * <p>
073         * @param cacheControl The cache which the created instance accesses
074         */
075        public CacheAccess( CompositeCache<K, V> cacheControl )
076        {
077            this.cacheControl = cacheControl;
078        }
079    
080        // ----------------------------- static methods for access to cache regions
081    
082        /**
083         * Define a new cache region with the given name. In the oracle specification, these attributes
084         * are global and not region specific, regional overrides is a value add each region should be
085         * able to house both cache and element attribute sets. It is more efficient to define a cache
086         * in the props file and then strictly use the get access method. Use of the define region
087         * outside of an initialization block should be avoided.
088         * <p>
089         * @param name Name that will identify the region
090         * @return CacheAccess instance for the new region
091         * @exception CacheException
092         */
093        public static <K extends Serializable, V extends Serializable> CacheAccess<K, V> defineRegion( String name )
094            throws CacheException
095        {
096            CompositeCache<K, V> cache = getCacheManager().getCache( name );
097            return new CacheAccess<K, V>( cache );
098        }
099    
100        /**
101         * Define a new cache region with the specified name and attributes.
102         * <p>
103         * @param name Name that will identify the region
104         * @param cattr CompositeCacheAttributes for the region
105         * @return CacheAccess instance for the new region
106         * @exception CacheException
107         */
108        public static <K extends Serializable, V extends Serializable> CacheAccess<K, V> defineRegion( String name, ICompositeCacheAttributes cattr )
109            throws CacheException
110        {
111            CompositeCache<K, V> cache = getCacheManager().getCache( name, cattr );
112            return new CacheAccess<K, V>( cache );
113        }
114    
115        /**
116         * Define a new cache region with the specified name and attributes and return a CacheAccess to
117         * it.
118         * <p>
119         * @param name Name that will identify the region
120         * @param cattr CompositeCacheAttributes for the region
121         * @param attr Attributes for the region
122         * @return CacheAccess instance for the new region
123         * @exception CacheException
124         */
125        public static <K extends Serializable, V extends Serializable> CacheAccess<K, V> defineRegion( String name, ICompositeCacheAttributes cattr, IElementAttributes attr )
126            throws CacheException
127        {
128            CompositeCache<K, V> cache = getCacheManager().getCache( name, cattr, attr );
129            return new CacheAccess<K, V>( cache );
130        }
131    
132        /**
133         * Get a CacheAccess instance for the given region.
134         * <p>
135         * @param region Name that identifies the region
136         * @return CacheAccess instance for region
137         * @exception CacheException
138         */
139        public static <K extends Serializable, V extends Serializable> CacheAccess<K, V> getAccess( String region )
140            throws CacheException
141        {
142            CompositeCache<K, V> cache = getCacheManager().getCache( region );
143            return new CacheAccess<K, V>( cache );
144        }
145    
146        /**
147         * Get a CacheAccess instance for the given region with the given attributes.
148         * <p>
149         * @param region Name that identifies the region
150         * @param icca
151         * @return CacheAccess instance for region
152         * @exception CacheException
153         */
154        public static <K extends Serializable, V extends Serializable> CacheAccess<K, V> getAccess( String region, ICompositeCacheAttributes icca )
155            throws CacheException
156        {
157            CompositeCache<K, V> cache = getCacheManager().getCache( region, icca );
158            return new CacheAccess<K, V>( cache );
159        }
160    
161        /**
162         * Helper method which checks to make sure the cacheMgr class field is set, and if not requests
163         * an instance from CacheManagerFactory.
164         *
165         * @throws CacheException if the configuration cannot be loaded
166         */
167        protected static CompositeCacheManager getCacheManager() throws CacheException
168        {
169            synchronized ( CacheAccess.class )
170            {
171                if ( cacheMgr == null )
172                {
173                    cacheMgr = CompositeCacheManager.getInstance();
174                }
175    
176                return cacheMgr;
177            }
178        }
179    
180        // ------------------------------------------------------- instance methods
181    
182        /**
183         * Retrieve an object from the cache region this instance provides access to.
184         * <p>
185         * @param name Key the object is stored as
186         * @return The object if found or null
187         */
188        public V get( K name )
189        {
190            ICacheElement<K, V> element = this.cacheControl.get( name );
191    
192            return ( element != null ) ? element.getVal() : null;
193        }
194    
195        /**
196         * Retrieve matching objects from the cache region this instance provides access to.
197         * <p>
198         * @param pattern - a key pattern for the objects stored
199         * @return A map of key to values.  These are stripped from the wrapper.
200         */
201        public Map<K, V> getMatching( String pattern )
202        {
203            HashMap<K, V> unwrappedResults = new HashMap<K, V>();
204    
205            Map<K, ICacheElement<K, V>> wrappedResults = this.cacheControl.getMatching( pattern );
206            if ( wrappedResults != null )
207            {
208                for (Map.Entry<K, ICacheElement<K, V>> entry : wrappedResults.entrySet())
209                {
210                    ICacheElement<K, V> element = entry.getValue();
211                    if ( element != null )
212                    {
213                        unwrappedResults.put( entry.getKey(), element.getVal() );
214                    }
215                }
216            }
217            return unwrappedResults;
218        }
219    
220        /**
221         * This method returns the ICacheElement<K, V> wrapper which provides access to element info and other
222         * attributes.
223         * <p>
224         * This returns a reference to the wrapper. Any modifications will be reflected in the cache. No
225         * defensive copy is made.
226         * <p>
227         * This method is most useful if you want to determine things such as the how long the element
228         * has been in the cache.
229         * <p>
230         * The last access time in the ElementAttributes should be current.
231         * <p>
232         * @param name Key the Serializable is stored as
233         * @return The ICacheElement<K, V> if the object is found or null
234         */
235        public ICacheElement<K, V> getCacheElement( K name )
236        {
237            return this.cacheControl.get( name );
238        }
239    
240        /**
241         * Get multiple elements from the cache based on a set of cache keys.
242         * <p>
243         * This method returns the ICacheElement<K, V> wrapper which provides access to element info and other
244         * attributes.
245         * <p>
246         * This returns a reference to the wrapper. Any modifications will be reflected in the cache. No
247         * defensive copy is made.
248         * <p>
249         * This method is most useful if you want to determine things such as the how long the element
250         * has been in the cache.
251         * <p>
252         * The last access time in the ElementAttributes should be current.
253         * <p>
254         * @param names set of Serializable cache keys
255         * @return a map of K key to ICacheElement<K, V> element, or empty map if none of the keys are present
256         */
257        public Map<K, ICacheElement<K, V>> getCacheElements( Set<K> names )
258        {
259            return this.cacheControl.getMultiple( names );
260        }
261    
262        /**
263         * Get multiple elements from the cache based on a set of cache keys.
264         * <p>
265         * This method returns the ICacheElement<K, V> wrapper which provides access to element info and other
266         * attributes.
267         * <p>
268         * This returns a reference to the wrapper. Any modifications will be reflected in the cache. No
269         * defensive copy is made.
270         * <p>
271         * This method is most useful if you want to determine things such as the how long the element
272         * has been in the cache.
273         * <p>
274         * The last access time in the ElementAttributes should be current.
275         * <p>
276         * @param pattern key search pattern
277         * @return a map of K key to ICacheElement<K, V> element, or empty map if no keys match the pattern
278         */
279        public Map<K, ICacheElement<K, V>> getMatchingCacheElements( String pattern )
280        {
281            return this.cacheControl.getMatching( pattern );
282        }
283    
284        /**
285         * Place a new object in the cache, associated with key name. If there is currently an object
286         * associated with name in the region an ObjectExistsException is thrown. Names are scoped to a
287         * region so they must be unique within the region they are placed.
288         * <p>
289         * @param key Key object will be stored with
290         * @param value Object to store
291         * @exception CacheException and ObjectExistsException is thrown if the item is already in the
292         *                cache.
293         */
294        public void putSafe( K key, V value )
295            throws CacheException
296        {
297            if ( this.cacheControl.get( key ) != null )
298            {
299                throw new ObjectExistsException( "putSafe failed.  Object exists in the cache for key [" + key
300                    + "].  Remove first or use a non-safe put to override the value." );
301            }
302            put( key, value );
303        }
304    
305        /**
306         * Place a new object in the cache, associated with key name. If there is currently an object
307         * associated with name in the region it is replaced. Names are scoped to a region so they must
308         * be unique within the region they are placed. ObjectExistsException
309         * @param name Key object will be stored with
310         * @param obj Object to store
311         * @exception CacheException
312         */
313        public void put( K name, V obj )
314            throws CacheException
315        {
316            // Call put with a copy of the contained caches default attributes.
317            // the attributes are copied by the cacheControl
318            put( name, obj, this.cacheControl.getElementAttributes() );
319        }
320    
321        /**
322         * Constructs a cache element with these attributes, and puts it into the cache.
323         * <p>
324         * If the key or the value is null, and InvalidArgumentException is thrown.
325         * <p>
326         * @see org.apache.jcs.access.behavior.ICacheAccess#put(java.lang.Object, java.lang.Object,
327         *      org.apache.jcs.engine.behavior.IElementAttributes)
328         */
329        public void put( K key, V val, IElementAttributes attr )
330            throws CacheException
331        {
332            if ( key == null )
333            {
334                throw new InvalidArgumentException( "Key must not be null" );
335            }
336    
337            if ( val == null )
338            {
339                throw new InvalidArgumentException( "Value must not be null" );
340            }
341    
342            // Create the element and update. This may throw an IOException which
343            // should be wrapped by cache access.
344            try
345            {
346                CacheElement<K, V> ce = new CacheElement<K, V>( this.cacheControl.getCacheName(), key,
347                                                    val );
348    
349                ce.setElementAttributes( attr );
350    
351                this.cacheControl.update( ce );
352            }
353            catch ( Exception e )
354            {
355                throw new CacheException( e );
356            }
357        }
358    
359        /**
360         * Destroy the region and all objects within it. After calling this method, the Cache object can
361         * no longer be used as it will be closed.
362         * <p>
363         * @exception CacheException
364         * @deprecated
365         */
366        @Deprecated
367        public void destroy()
368            throws CacheException
369        {
370            try
371            {
372                this.cacheControl.removeAll();
373            }
374            catch ( IOException e )
375            {
376                throw new CacheException( e );
377            }
378        }
379    
380        /**
381         * Removes all of the elements from a region.
382         * <p>
383         * @deprecated use clear()
384         * @throws CacheException
385         */
386        @Deprecated
387        public void remove()
388            throws CacheException
389        {
390            clear();
391        }
392    
393        /**
394         * Removes all of the elements from a region.
395         * <p>
396         * @throws CacheException
397         */
398        public void clear()
399            throws CacheException
400        {
401            try
402            {
403                this.cacheControl.removeAll();
404            }
405            catch ( IOException e )
406            {
407                throw new CacheException( e );
408            }
409        }
410    
411        /**
412         * Invalidate all objects associated with key name, removing all references to the objects from
413         * the cache.
414         * <p>
415         * @param name Key that specifies object to invalidate
416         * @exception CacheException
417         * @deprecated use remove
418         */
419        @Deprecated
420        public void destroy( K name )
421            throws CacheException
422        {
423            this.cacheControl.remove( name );
424        }
425    
426        /**
427         * Removes a single item by name.
428         * <p>
429         * @param name the name of the item to remove.
430         * @throws CacheException
431         */
432        public void remove( K name )
433            throws CacheException
434        {
435            this.cacheControl.remove( name );
436        }
437    
438        /**
439         * ResetAttributes allows for some of the attributes of a region to be reset in particular
440         * expiration time attributes, time to live, default time to live and idle time, and event
441         * handlers. Changing default settings on groups and regions will not affect existing objects.
442         * Only object loaded after the reset will use the new defaults. If no name argument is
443         * provided, the reset is applied to the region.
444         * <p>
445         * NOTE: this method is does not reset the attributes for items already in the cache. It could
446         * potentially do this for items in memory, and maybe on disk (which would be slow) but not
447         * remote items. Rather than have unpredictable behavior, this method just sets the default
448         * attributes.
449         * <p>
450         * TODO is should be renamed "setDefaultElementAttributes"
451         * <p>
452         * @deprecated As of release 1.3
453         * @see #setDefaultElementAttributes(IElementAttributes)
454         * @param attr New attributes for this region.
455         * @exception CacheException
456         * @exception InvalidHandleException
457         */
458        @Deprecated
459        public void resetElementAttributes( IElementAttributes attr )
460            throws CacheException, InvalidHandleException
461        {
462            this.cacheControl.setElementAttributes( attr );
463        }
464    
465        /**
466         * This method is does not reset the attributes for items already in the cache. It could
467         * potentially do this for items in memory, and maybe on disk (which would be slow) but not
468         * remote items. Rather than have unpredictable behavior, this method just sets the default
469         * attributes. Items subsequently put into the cache will use these defaults if they do not
470         * specify specific attributes.
471         * <p>
472         * @param attr the default attributes.
473         * @throws CacheException if something goes wrong.
474         */
475        public void setDefaultElementAttributes( IElementAttributes attr )
476            throws CacheException
477        {
478            this.cacheControl.setElementAttributes( attr );
479        }
480    
481        /**
482         * Reset attributes for a particular element in the cache. NOTE: this method is currently not
483         * implemented.
484         * <p>
485         * @param name Key of object to reset attributes for
486         * @param attr New attributes for the object
487         * @exception CacheException
488         * @exception InvalidHandleException if the item does not exist.
489         */
490        public void resetElementAttributes( K name, IElementAttributes attr )
491            throws CacheException, InvalidHandleException
492        {
493            ICacheElement<K, V> element = this.cacheControl.get( name );
494    
495            if ( element == null )
496            {
497                throw new InvalidHandleException( "Object for name [" + name + "] is not in the cache" );
498            }
499    
500            // Although it will work currently, don't assume pass by reference here,
501            // i.e. don't do this:
502            // element.setElementAttributes( attr );
503            // Another reason to call put is to force the changes to be distributed.
504    
505            put( element.getKey(), element.getVal(), attr );
506        }
507    
508        /**
509         * GetElementAttributes will return an attribute object describing the current attributes
510         * associated with the object name.
511         * <p>
512         * This was confusing, so I created a new method with a clear name.
513         * <p>
514         * @deprecated As of release 1.3
515         * @see #getDefaultElementAttributes
516         * @return Attributes for this region
517         * @exception CacheException
518         */
519        @Deprecated
520        public IElementAttributes getElementAttributes()
521            throws CacheException
522        {
523            return this.cacheControl.getElementAttributes();
524        }
525    
526        /**
527         * Retrieves A COPY OF the default element attributes used by this region. This does not provide
528         * a reference to the element attributes.
529         * <p>
530         * Each time an element is added to the cache without element attributes, the default element
531         * attributes are cloned.
532         * <p>
533         * @return the deafualt element attributes used by this region.
534         * @throws CacheException
535         */
536        public IElementAttributes getDefaultElementAttributes()
537            throws CacheException
538        {
539            return this.cacheControl.getElementAttributes();
540        }
541    
542        /**
543         * GetElementAttributes will return an attribute object describing the current attributes
544         * associated with the object name. The name object must override the Object.equals and
545         * Object.hashCode methods.
546         * <p>
547         * @param name Key of object to get attributes for
548         * @return Attributes for the object, null if object not in cache
549         * @exception CacheException
550         */
551        public IElementAttributes getElementAttributes( K name )
552            throws CacheException
553        {
554            IElementAttributes attr = null;
555    
556            try
557            {
558                attr = this.cacheControl.getElementAttributes( name );
559            }
560            catch ( IOException ioe )
561            {
562                log.error( "Failure getting element attributes", ioe );
563            }
564    
565            return attr;
566        }
567    
568        /**
569         * This returns the ICacheStats object with information on this region and its auxiliaries.
570         * <p>
571         * This data can be formatted as needed.
572         * <p>
573         * @return ICacheStats
574         */
575        public ICacheStats getStatistics()
576        {
577            return this.cacheControl.getStatistics();
578        }
579    
580        /**
581         * @return A String version of the stats.
582         */
583        public String getStats()
584        {
585            return this.cacheControl.getStats();
586        }
587    
588        /**
589         * Dispose this region. Flushes objects to and closes auxiliary caches. This is a shutdown
590         * command!
591         * <p>
592         * To simply remove all elements from the region use clear().
593         */
594        public void dispose()
595        {
596            this.cacheControl.dispose();
597        }
598    
599        /**
600         * Gets the ICompositeCacheAttributes of the cache region.
601         * <p>
602         * @return ICompositeCacheAttributes, the controllers config info, defined in the top section of
603         *         a region definition.
604         */
605        public ICompositeCacheAttributes getCacheAttributes()
606        {
607            return this.cacheControl.getCacheAttributes();
608        }
609    
610        /**
611         * Sets the ICompositeCacheAttributes of the cache region.
612         * <p>
613         * @param cattr The new ICompositeCacheAttribute value
614         */
615        public void setCacheAttributes( ICompositeCacheAttributes cattr )
616        {
617            this.cacheControl.setCacheAttributes( cattr );
618        }
619    
620        /**
621         * This instructs the memory cache to remove the <i>numberToFree</i> according to its eviction
622         * policy. For example, the LRUMemoryCache will remove the <i>numberToFree</i> least recently
623         * used items. These will be spooled to disk if a disk auxiliary is available.
624         * <p>
625         * @param numberToFree
626         * @return the number that were removed. if you ask to free 5, but there are only 3, you will
627         *         get 3.
628         * @throws CacheException
629         */
630        public int freeMemoryElements( int numberToFree )
631            throws CacheException
632        {
633            int numFreed = -1;
634            try
635            {
636                numFreed = this.cacheControl.getMemoryCache().freeElements( numberToFree );
637            }
638            catch ( IOException ioe )
639            {
640                String message = "Failure freeing memory elements.  ";
641                log.error( message, ioe );
642                throw new CacheException( message + ioe.getMessage() );
643            }
644            return numFreed;
645        }
646    }