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}