001//
002// Name
003//  $RCSfile: CacheManager.java,v $
004// 
005//  Copyright 2014-2015 Cloud Software Group, Inc. ALL RIGHTS RESERVED. 
006//  Cloud Software Group, Inc. Confidential Information
007//
008// History
009//  $Revision: 1.1.2.12 $ $Date: 2015/02/06 19:44:42 $
010//
011package com.kabira.platform;
012
013import com.kabira.platform.flusher.FlushManager;
014import com.kabira.platform.cache.CacheManagerUtils;
015import java.util.ArrayList;
016
017/**
018 * The CacheManager class. This is the main interface for managing
019 * named caches.
020 */
021public final class CacheManager
022{
023    /**
024     * Exception thrown when a Cache instance has been destroyed.
025     */
026    public final static class CacheDestroyed extends java.lang.Error
027    {
028        /**
029         *  Serialization version.
030         */
031        public final static long serialVersionUID = 1L;
032
033        /**
034         *  Creates a CacheDestroyed exception.
035         *  @param  message String to include in the exception.
036         */
037        public CacheDestroyed(String message)
038        {
039            super(message);
040        }
041    }
042
043    /**
044     * A Cache instance.
045     * <p>
046     * Created by the CacheManager, it is the main interface for configuring
047     * and accessing information for a named cache.
048     * <p>
049     * When created, the default cache size is unlimited. 
050     */
051    public final static class Cache
052    {
053        /**
054         * Associate the given class to a cache.
055         *
056         * Also associates any classes that extend the class, which
057         * are not already associated with another named cache.
058         * <p>
059         * If the class is already associated with a named cache, it will
060         * be removed from that cache, and added to this one.
061         * If it is already associated with this cache, no changes are made.
062         * @param klass Managed class to add.
063         * @exception ManagedClassError Thrown if <code>klass</code>
064         *   is not Managed.
065         * @exception CacheDestroyed This cache instance no longer exists.
066         */
067        public void addClass(Class<?> klass)
068            throws ManagedClassError, CacheDestroyed
069        {
070            ManagedObject.checkManagedClass(klass, "addClass");
071            try
072            {
073                CacheManagerUtils.addType(m_name, klass.getName());
074            }
075            catch (ResourceUnavailableException ex)
076            {
077                throw new CacheDestroyed(ex.getMessage());
078            }
079        }
080        /**
081         * Set the size of the cache in bytes.
082         * <p>
083         * The system cache flusher asynchronously attempts to keep
084         * memory utilization at or below this level for objects in this
085         * cache. Note that this is not a strict size limit; object sizes
086         * will be estimated, and the flusher may run infrequently,
087         * allowing the cache to exceed the size setting.
088         * @param size size of cache in bytes.
089         * @exception IllegalArgumentException
090         *  The size is negative or is greater than the shared memory that
091         *  is available.
092         * @exception CacheDestroyed This cache instance no longer exists.
093         */
094        public void setSizeBytes(long size)
095            throws IllegalArgumentException, CacheDestroyed
096        {
097            if (size < 0 || size > m_memorySize)
098            {
099                throw new IllegalArgumentException("Invalid size " + size +
100                    ", maximum size allowed is " + m_memorySize);
101            }
102
103            try
104            {
105                CacheManagerUtils.setSizeBytes(m_name, size);
106            }
107            catch (ResourceUnavailableException ex)
108            {
109                throw new CacheDestroyed(ex.getMessage());
110            }
111        }
112
113        /**
114         * Set the size of the cache as a percentage.
115         * <p>
116         * A decimal number between 1 and 100.
117         * Indicates the maximum percentage of shared memory
118         * within the node to be used for this named cache.
119         * <p>
120         * If a percent of 100 is given, the cache will have no limit.
121         * @param percent Percent of shared memory to use for this cache.
122         * @exception IllegalArgumentException
123         *  An invalid percent was passed in.
124         * @exception CacheDestroyed This cache instance no longer exists.
125         */
126        public void setSizePercent(int percent) throws CacheDestroyed
127        {
128            if (percent < 1 || percent > 100)
129            {
130                throw new IllegalArgumentException(
131                    "Invalid percent " + percent);
132            }
133
134            try
135            {
136                CacheManagerUtils.setSizeBytes(m_name,
137                        (percent * m_memorySize) / 100);
138            }
139            catch (ResourceUnavailableException ex)
140            {
141                throw new CacheDestroyed(ex.getMessage());
142            }
143        }
144        /**
145         * Disable cache.
146         * <p>
147         * This disables further caching of objects. The flusher will
148         * asynchronously remove objects if the cache utilization is
149         * greater than the size, otherwise the currently cached
150         * instances will remain. If the cache is already disabled, no
151         * action is taken.
152         * @exception CacheDestroyed This cache instance no longer exists.
153         */
154        public void disable() throws CacheDestroyed
155        {
156            try
157            {
158                CacheManagerUtils.enable(m_name, false);
159            }
160            catch (ResourceUnavailableException ex)
161            {
162                throw new CacheDestroyed(ex.getMessage());
163            }
164        }
165        /**
166         * Enable cache.
167         * <p>
168         * Enable caching of objects using the current configuration of this
169         * named cache. If the cache is already enabled, no action is taken.
170         * @exception CacheDestroyed This cache instance no longer exists.
171         */
172        public void enable() throws CacheDestroyed
173        {
174            try
175            {
176                CacheManagerUtils.enable(m_name, true);
177            }
178            catch (ResourceUnavailableException ex)
179            {
180                throw new CacheDestroyed(ex.getMessage());
181            }
182        }
183
184        /**
185         * Flush a cache.
186         * <p>
187         * This immediately flushes instances from the cache until the 
188         * cache size criteria are met. Note that this may lock a
189         * significant number of object instances in the caller's
190         * transaction and should be used with care.
191         * <p>
192         * Index values are also removed from shared memory
193         * when the object is flushed.
194         * @param numberObjects Maximum number of objects to flush. A value
195         *  of zero means to flush as many instances as needed. 
196         * @return Number of instances flushed.
197         * @exception IllegalArgumentException
198         *  The numberObjects value is negative.
199         * @exception CacheDestroyed This cache instance no longer exists.
200         */
201        public long flush(long numberObjects)
202                throws IllegalArgumentException, CacheDestroyed
203        {
204            checkNegative(numberObjects, "numberObjects");
205            try
206            {
207                return FlushManager.flushCache(m_name, numberObjects);
208            }
209            catch (ResourceUnavailableException ex)
210            {
211                throw new CacheDestroyed(ex.getMessage());
212            }
213        }
214
215        /**
216         * Return the name of the cache.
217         * @return String containing the name.
218         * @exception CacheDestroyed This cache instance no longer exists.
219         */
220        public String getName() throws CacheDestroyed
221        {
222            // FIX THIS: Using isDisabled to throw CacheDestroyed
223            isDisabled();
224            return m_name;
225        }
226        /**
227         * Return all known classes associated with this cache.
228         * @return <code>Iterable<klass></code> containing all classes.
229         * @exception CacheDestroyed This cache instance no longer exists.
230         */
231        public Iterable<Class<?>> getClasses() throws CacheDestroyed
232        {
233            String [] classNames;
234            
235            try
236            {
237                classNames = CacheManagerUtils.getTypes(m_name);
238            }
239            catch (ResourceUnavailableException ex)
240            {
241                throw new CacheDestroyed(ex.getMessage());
242            }
243
244            ArrayList<Class<?>> classes =
245                    new ArrayList<Class<?>>(classNames.length);
246            for (int i = 0; i < classNames.length; i++)
247            {
248                try
249                {
250                    classes.add(Class.forName(classNames[i]));
251                }
252                catch (ClassNotFoundException ex)
253                {
254                    ex.printStackTrace();
255                }
256            }
257            return classes;
258        }
259        /**
260         * Return if the cache is currently disabled.
261         * @return True if the cache is disabled, otherwise false.
262         * @exception CacheDestroyed This cache instance no longer exists.
263         */
264        public boolean isDisabled() throws CacheDestroyed
265        {
266            try
267            {
268                return CacheManagerUtils.isDisabled(m_name);
269            }
270            catch (ResourceUnavailableException ex)
271            {
272                throw new CacheDestroyed(ex.getMessage());
273            }
274        }
275
276        /**
277         * Return the number of instances currently cached.
278         * @return Number of instances in memory.
279         * @exception CacheDestroyed This cache instance no longer exists.
280         */
281        public long getNumberInstances() throws CacheDestroyed
282        {
283            try
284            {
285                return CacheManagerUtils.getNumberInstances(m_name);
286            }
287            catch (ResourceUnavailableException ex)
288            {
289                throw new CacheDestroyed(ex.getMessage());
290            }
291        }
292
293        /**
294         * Return the cache size in bytes.
295         * @return The current cache size in bytes.
296         * @exception CacheDestroyed This cache instance no longer exists.
297         */
298        public long getCacheSizeBytes() throws CacheDestroyed
299        {
300            try
301            {
302                return CacheManagerUtils.getCacheSizeBytes(m_name);
303            }
304            catch (ResourceUnavailableException ex)
305            {
306                throw new CacheDestroyed(ex.getMessage());
307            }
308        }
309
310        /**
311         * Return the amount of cache used as a percent.
312         * <p>
313         * If the cache setting has no limit, a value of 0 is returned.
314         * To get the percentage of total shared memory used, use the {@link
315         * Cache#getMemoryUtilization()} method.
316         * <p>
317         * Since the flushing of objects from the cache is asynchronous,
318         * values larger than 100 percent can be returned from this
319         * method.
320         * @return Percentage of cache used.
321         * @exception CacheDestroyed This cache instance no longer exists.
322         */
323        public int getCacheUtilization() throws CacheDestroyed
324        {
325            long    cacheSize = getCacheSizeBytes();
326
327            if (cacheSize == m_memorySize || cacheSize == 0)
328            {
329                return 0;
330            }
331
332            long memoryUsage;
333
334            try
335            {
336                memoryUsage = CacheManagerUtils.getMemoryUsage(m_name);
337            }
338            catch (ResourceUnavailableException ex)
339            {
340                throw new CacheDestroyed(ex.getMessage());
341            }
342
343            return (int)((100 * memoryUsage)/cacheSize);
344        }
345        /**
346         * Return the amount of shared memory used as a percent.
347         * <p>
348         * The percentage of the node's shared memory currently in use by
349         * this cache will be calculated and returned. This value will be
350         * approximate.
351         * @return Percentage of shared memory used.
352         * @exception CacheDestroyed This cache instance no longer exists.
353         */
354        public int getMemoryUtilization() throws CacheDestroyed
355        {
356            long memoryUsage;
357
358            try
359            {
360                memoryUsage = CacheManagerUtils.getMemoryUsage(m_name);
361            }
362            catch (ResourceUnavailableException ex)
363            {
364                throw new CacheDestroyed(ex.getMessage());
365            }
366
367            return (int)((100 * memoryUsage)/m_memorySize);
368        }
369
370        //
371        // Private constructor
372        //
373        private Cache(String name, long memorySize)
374        {
375            m_name = name;
376            m_memorySize = memorySize;
377        }
378
379        //
380        // Private fields
381        //
382        private String      m_name;
383        private long        m_memorySize;
384    }
385
386    /**
387     * The Cache flusher.
388     * <p>
389     * Configure the asynchronous thread that periodically
390     * removes instances from all active caches, flush notifications,
391     * and support for explicit object flushing.
392     */
393    public final static class CacheFlusher
394    {
395        /**
396         * Set the number of seconds to sleep between runs of the flusher.
397         * <p>
398         * The default value is to have the flusher run every second, a
399         * value of 0 disables the flusher. Note that disabling the
400         * flusher will allow caches bounded by size to exceed their
401         * configured sizes.
402         * @param seconds Number of seconds.
403         * @exception IllegalArgumentException
404         *  The seconds value is negative.
405         */
406        public void setFlushIntervalSeconds(long seconds)
407            throws IllegalArgumentException
408        {
409            checkNegative(seconds, "seconds");
410            CacheManagerUtils.setFlushIntervalSeconds(seconds);
411        }
412        /**
413         * Set the maximum number of object instances to flush.
414         * <p>
415         * Sets the number of object instances to flush each time the
416         * flush thread executes. A value of 0 indicates no limit.
417         * @param numberObjects Maximum number of objects to flush.
418         * @exception IllegalArgumentException
419         *  The numberObjects value is negative.
420         */
421        public void setMaximumObjectsPerFlush(long numberObjects)
422            throws IllegalArgumentException
423        {
424            checkNegative(numberObjects, "numberObjects");
425            CacheManagerUtils.setMaxObjectsPerFlush(numberObjects);
426        }
427
428        /**
429         * Associate a flush notifier with a Managed class.
430         * <p>
431         * The Managed class is the type T from the user implementation
432         * of Notifier<T>.
433         * <p>
434         * If a Notifier instance is already associated with a class,
435         * the new value overwrites the previous one. The lifecycle of
436         * the returned instance is not affected.
437         * <p>
438         * The system automatically unregisters the passed in Notifier
439         * instance when this JVM shutdowns down. The lifecycle of
440         * the Notifier instance is not affected. If the JVM is
441         * restarted, the Notifer is not automatically reregistered.
442         * <p>
443         * <b>Inheritance</b>
444         * <p>
445         * The Notifier will also be used for any classes which
446         * extend type T which don't have their own Notifier set.
447         * <p>
448         * During an object flush, if there are Notifiers set on
449         * multiple classes in the inheritance chain, only a single
450         * Notifier will be called.
451         * The Notifier is selected by first looking at the target
452         * object's class, and if no Notifier is set, searching up
453         * the inheritance chain until a Notifier is found.
454         *
455         * @param notifier User defined Notifier instance.
456         *
457         * @return Previous Notifier instance associated with
458         * the notifier's managed type, or null if no Notifier was set
459         * for that type.
460         *
461         * @exception ManagedClassError
462         *  The managedClass is not a Managed object.
463         *
464         * @see CacheFlusher#clearNotifier
465         */
466        public static final com.kabira.platform.flusher.Notifier<?> setNotifier(
467            final com.kabira.platform.flusher.Notifier<?> notifier)
468            throws ManagedClassError
469        {
470            return FlushManager.setNotifier(notifier);
471        }
472
473        /**
474         * Clear the flush notifier for the given managed class.
475         * <p>
476         * If no flush notifier is associated with the managed class,
477         * this operation does nothing.
478         *<p>
479         * Notifiers registered for parent or child classes are not
480         * affected.
481         *
482         * @param managedClass Managed object class.
483         *
484         * @return Previous Notifier instance associated with
485         * the notifier's managed type, or null if no Notifier was set
486         * for that type.
487         *
488         * @exception ManagedClassError
489         *  The managedClass parameter is not a Managed class.
490         *
491         * @see CacheFlusher#setNotifier
492         */
493        public static final com.kabira.platform.flusher.Notifier<?>
494            clearNotifier(final Class<?> managedClass) throws ManagedClassError
495        {
496            try
497            {
498                return (com.kabira.platform.flusher.Notifier<?>)
499                    FlushManager.clearNotifier(managedClass);
500            }
501            catch (ResourceUnavailableException ex)
502            {
503                throw new ManagedClassError(ex.getMessage());
504            }
505        }
506
507        /** Flush a Managed object from shared memory.
508          * <p>
509          * If the operand is a partitioned object on the active node
510          * the flush is equivalent to ManagedObject.delete() and will
511          * remove the object and its keys for all nodes in its partition.
512          * <p>
513          * If the operand is a partitioned object on a replica node,
514          * no action is taken.
515          * <p>
516          * If the operand is a non-partitioned, remote object, it
517          * will be removed along with its keys, from the local node,
518          * where flush() is being invoked.
519          * <p>
520          * If the operand is a local object, flush is equivalent
521          * to ManagedObject.delete().
522          * <p><b>Delete Triggers</b> 
523          * <p>
524          * Any defined delete triggers will be called on the local
525          * node for flushes of local objects and partitioned objects
526          * on the active node.
527          * <p><b>Flush Notifier</b>
528          * <p>
529          * If a flush Notifier has been set for the class type of
530          * the operand (or a class in its parent inheritance chain),
531          * it is called before the flush occurs.
532          * If the Notifier.isFlushable() method returns false
533          * flush() returns immediately without having flushed
534          * the operand.
535          * See the setNotifier <b>Inheritance</b> section for
536          * for a description of how the Notifier is chosen.
537          *
538          * @param object Object to flush.
539          * @exception ManagedClassError if object is not Managed.
540          * @see CacheFlusher#setNotifier
541          * @see com.kabira.platform.flusher.Notifier
542          * @see com.kabira.platform.DeleteTrigger
543          */
544        public static void flush(Object object)
545            throws ManagedClassError
546        {
547            if (! ManagedObject.isManaged(object))
548            {
549                throw new ManagedClassError("Class "
550                    + object.getClass().getName()
551                    + " is not Managed. Cache.flush() can only be "
552                    + " applied to Managed classes.");
553            }
554
555            FlushManager.flush(object);
556        }
557
558        // Private constructor
559        private CacheFlusher() { }
560    }
561
562    /**
563     * Get or create a named cache.
564     * <p>
565     * This method creates a named Cache instance. If one already exists, that
566     * instance is returned.
567     * @param cacheName Name of Cache.
568     * @return Cache instance that was created or found.
569     */
570    public static Cache getOrCreateCache(final String cacheName)
571    {
572        long memorySize = _getOrCreateCache(cacheName);
573        return new Cache(cacheName, memorySize);
574    }
575
576    /**
577     * Delete a named cache.
578     * <p>
579     * This method deletes the named Cache instance. 
580     * <p>
581     * @param cacheName Name of a Cache to delete.
582     * @return True if cache instance was deleted, false if the named
583     *  cache was not found.
584     */
585    public static boolean deleteCache(final String cacheName)
586    {
587        return CacheManagerUtils.deleteCache(cacheName);
588    }
589
590    /**
591     * Return all known caches on this node.
592     * @return <code>Iterable<Cache></code> containing all caches.
593     */
594    public static Iterable<Cache> getCaches()
595    {
596        String [] cacheNames = CacheManagerUtils.getCacheNames();
597        ArrayList<Cache> caches = new ArrayList<Cache>(cacheNames.length);
598        for (int i = 0; i < cacheNames.length; i++)
599        {
600            caches.add(getOrCreateCache(cacheNames[i]));
601        }
602        return caches;
603    }
604
605    /**
606     * Get the cache flusher.
607     * <p>
608     * This method returns the CacheFlusher instance. 
609     * @return Active CacheFlusher instance.
610     */
611    public static CacheFlusher getCacheFlusher()
612    {
613        return new CacheFlusher();
614    }
615
616    // Private constructor, never executed.
617    private CacheManager() { }
618
619    private static void checkNegative(long val, String param)
620        throws IllegalArgumentException
621    {
622        if (val < 0)
623        {
624            throw new IllegalArgumentException(
625                "Invalid " + param + " " + val);
626        }
627    }
628
629    private static long _getOrCreateCache(String name)
630    {
631        return CacheManagerUtils.getOrCreateReferenceCache(name);
632    }
633}