1 package org.apache.commons.jcs.auxiliary.disk; 2 3 /* 4 * Licensed to the Apache Software Foundation (ASF) under one 5 * or more contributor license agreements. See the NOTICE file 6 * distributed with this work for additional information 7 * regarding copyright ownership. The ASF licenses this file 8 * to you under the Apache License, Version 2.0 (the 9 * "License"); you may not use this file except in compliance 10 * with the License. You may obtain a copy of the License at 11 * 12 * http://www.apache.org/licenses/LICENSE-2.0 13 * 14 * Unless required by applicable law or agreed to in writing, 15 * software distributed under the License is distributed on an 16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 * KIND, either express or implied. See the License for the 18 * specific language governing permissions and limitations 19 * under the License. 20 */ 21 22 import java.io.IOException; 23 import java.util.ArrayList; 24 import java.util.HashMap; 25 import java.util.HashSet; 26 import java.util.Map; 27 import java.util.Set; 28 import java.util.concurrent.locks.ReentrantReadWriteLock; 29 30 import org.apache.commons.jcs.auxiliary.AbstractAuxiliaryCacheEventLogging; 31 import org.apache.commons.jcs.auxiliary.AuxiliaryCache; 32 import org.apache.commons.jcs.auxiliary.disk.behavior.IDiskCacheAttributes; 33 import org.apache.commons.jcs.engine.CacheEventQueueFactory; 34 import org.apache.commons.jcs.engine.CacheInfo; 35 import org.apache.commons.jcs.engine.CacheStatus; 36 import org.apache.commons.jcs.engine.behavior.ICache; 37 import org.apache.commons.jcs.engine.behavior.ICacheElement; 38 import org.apache.commons.jcs.engine.behavior.ICacheEventQueue; 39 import org.apache.commons.jcs.engine.behavior.ICacheListener; 40 import org.apache.commons.jcs.engine.stats.StatElement; 41 import org.apache.commons.jcs.engine.stats.Stats; 42 import org.apache.commons.jcs.engine.stats.behavior.IStatElement; 43 import org.apache.commons.jcs.engine.stats.behavior.IStats; 44 import org.apache.commons.jcs.utils.struct.LRUMap; 45 import org.apache.commons.logging.Log; 46 import org.apache.commons.logging.LogFactory; 47 48 /** 49 * Abstract class providing a base implementation of a disk cache, which can be easily extended to 50 * implement a disk cache for a specific persistence mechanism. 51 * 52 * When implementing the abstract methods note that while this base class handles most things, it 53 * does not acquire or release any locks. Implementations should do so as necessary. This is mainly 54 * done to minimize the time spent in critical sections. 55 * 56 * Error handling in this class needs to be addressed. Currently if an exception is thrown by the 57 * persistence mechanism, this class destroys the event queue. Should it also destroy purgatory? 58 * Should it dispose itself? 59 */ 60 public abstract class AbstractDiskCache<K, V> 61 extends AbstractAuxiliaryCacheEventLogging<K, V> 62 { 63 /** The logger */ 64 private static final Log log = LogFactory.getLog( AbstractDiskCache.class ); 65 66 /** Generic disk cache attributes */ 67 private IDiskCacheAttributes diskCacheAttributes = null; 68 69 /** 70 * Map where elements are stored between being added to this cache and actually spooled to disk. 71 * This allows puts to the disk cache to return quickly, and the more expensive operation of 72 * serializing the elements to persistent storage queued for later. 73 * 74 * If the elements are pulled into the memory cache while the are still in purgatory, writing to 75 * disk can be canceled. 76 */ 77 private Map<K, PurgatoryElement<K, V>> purgatory; 78 79 /** 80 * The CacheEventQueue where changes will be queued for asynchronous updating of the persistent 81 * storage. 82 */ 83 private ICacheEventQueue<K, V> cacheEventQueue; 84 85 /** 86 * Indicates whether the cache is 'alive': initialized, but not yet disposed. Child classes must 87 * set this to true. 88 */ 89 private boolean alive = false; 90 91 /** Every cache will have a name, subclasses must set this when they are initialized. */ 92 private String cacheName; 93 94 /** DEBUG: Keeps a count of the number of purgatory hits for debug messages */ 95 private int purgHits = 0; 96 97 /** 98 * We lock here, so that we cannot get an update after a remove all. an individual removal locks 99 * the item. 100 */ 101 private final ReentrantReadWriteLock removeAllLock = new ReentrantReadWriteLock(); 102 103 // ----------------------------------------------------------- constructors 104 105 /** 106 * Construct the abstract disk cache, create event queues and purgatory. Child classes should 107 * set the alive flag to true after they are initialized. 108 * 109 * @param attr 110 */ 111 protected AbstractDiskCache( IDiskCacheAttributes attr ) 112 { 113 this.diskCacheAttributes = attr; 114 this.cacheName = attr.getCacheName(); 115 116 // create queue 117 CacheEventQueueFactory<K, V> fact = new CacheEventQueueFactory<K, V>(); 118 this.cacheEventQueue = fact.createCacheEventQueue( new MyCacheListener(), CacheInfo.listenerId, cacheName, 119 diskCacheAttributes.getEventQueuePoolName(), 120 diskCacheAttributes.getEventQueueType() ); 121 122 // create purgatory 123 initPurgatory(); 124 } 125 126 /** 127 * @return true if the cache is alive 128 */ 129 public boolean isAlive() 130 { 131 return alive; 132 } 133 134 /** 135 * @param alive set the alive status 136 */ 137 public void setAlive(boolean alive) 138 { 139 this.alive = alive; 140 } 141 142 /** 143 * Purgatory size of -1 means to use a HashMap with no size limit. Anything greater will use an 144 * LRU map of some sort. 145 * 146 * TODO Currently setting this to 0 will cause nothing to be put to disk, since it will assume 147 * that if an item is not in purgatory, then it must have been plucked. We should make 0 148 * work, a way to not use purgatory. 149 */ 150 private void initPurgatory() 151 { 152 // we need this so we can stop the updates from happening after a 153 // removeall 154 removeAllLock.writeLock().lock(); 155 156 try 157 { 158 synchronized (this) 159 { 160 if ( diskCacheAttributes.getMaxPurgatorySize() >= 0 ) 161 { 162 purgatory = new LRUMap<K, PurgatoryElement<K, V>>( diskCacheAttributes.getMaxPurgatorySize() ); 163 } 164 else 165 { 166 purgatory = new HashMap<K, PurgatoryElement<K, V>>(); 167 } 168 } 169 } 170 finally 171 { 172 removeAllLock.writeLock().unlock(); 173 } 174 } 175 176 // ------------------------------------------------------- interface ICache 177 178 /** 179 * Adds the provided element to the cache. Element will be added to purgatory, and then queued 180 * for later writing to the serialized storage mechanism. 181 * 182 * An update results in a put event being created. The put event will call the handlePut method 183 * defined here. The handlePut method calls the implemented doPut on the child. 184 * 185 * @param cacheElement 186 * @throws IOException 187 * @see org.apache.commons.jcs.engine.behavior.ICache#update 188 */ 189 @Override 190 public final void update( ICacheElement<K, V> cacheElement ) 191 throws IOException 192 { 193 if ( log.isDebugEnabled() ) 194 { 195 log.debug( "Putting element in purgatory, cacheName: " + cacheName + ", key: " + cacheElement.getKey() ); 196 } 197 198 try 199 { 200 // Wrap the CacheElement in a PurgatoryElement 201 PurgatoryElement<K, V> pe = new PurgatoryElement<K, V>( cacheElement ); 202 203 // Indicates the the element is eligible to be spooled to disk, 204 // this will remain true unless the item is pulled back into 205 // memory. 206 pe.setSpoolable( true ); 207 208 // Add the element to purgatory 209 synchronized ( purgatory ) 210 { 211 purgatory.put( pe.getKey(), pe ); 212 } 213 214 // Queue element for serialization 215 cacheEventQueue.addPutEvent( pe ); 216 } 217 catch ( IOException ex ) 218 { 219 log.error( "Problem adding put event to queue.", ex ); 220 221 cacheEventQueue.destroy(); 222 } 223 } 224 225 /** 226 * Check to see if the item is in purgatory. If so, return it. If not, check to see if we have 227 * it on disk. 228 * 229 * @param key 230 * @return ICacheElement<K, V> or null 231 * @see AuxiliaryCache#get 232 */ 233 @Override 234 public final ICacheElement<K, V> get( K key ) 235 { 236 // If not alive, always return null. 237 238 if ( !alive ) 239 { 240 if ( log.isDebugEnabled() ) 241 { 242 log.debug( "get was called, but the disk cache is not alive." ); 243 } 244 return null; 245 } 246 247 PurgatoryElement<K, V> pe = null; 248 synchronized ( purgatory ) 249 { 250 pe = purgatory.get( key ); 251 } 252 253 // If the element was found in purgatory 254 if ( pe != null ) 255 { 256 purgHits++; 257 258 if ( log.isDebugEnabled() ) 259 { 260 if ( purgHits % 100 == 0 ) 261 { 262 log.debug( "Purgatory hits = " + purgHits ); 263 } 264 } 265 266 // Since the element will go back to the memory cache, we could set 267 // spoolable to false, which will prevent the queue listener from 268 // serializing the element. This would not match the disk cache 269 // behavior and the behavior of other auxiliaries. Gets never remove 270 // items from auxiliaries. 271 // Beyond consistency, the items should stay in purgatory and get 272 // spooled since the mem cache may be set to 0. If an item is 273 // active, it will keep getting put into purgatory and removed. The 274 // CompositeCache now does not put an item to memory from disk if 275 // the size is 0. 276 // Do not set spoolable to false. Just let it go to disk. This 277 // will allow the memory size = 0 setting to work well. 278 279 if ( log.isDebugEnabled() ) 280 { 281 log.debug( "Found element in purgatory, cacheName: " + cacheName + ", key: " + key ); 282 } 283 284 return pe.getCacheElement(); 285 } 286 287 // If we reach this point, element was not found in purgatory, so get 288 // it from the cache. 289 try 290 { 291 return doGet( key ); 292 } 293 catch ( Exception e ) 294 { 295 log.error( e ); 296 297 cacheEventQueue.destroy(); 298 } 299 300 return null; 301 } 302 303 /** 304 * Gets items from the cache matching the given pattern. Items from memory will replace those 305 * from remote sources. 306 * 307 * This only works with string keys. It's too expensive to do a toString on every key. 308 * 309 * Auxiliaries will do their best to handle simple expressions. For instance, the JDBC disk 310 * cache will convert * to % and . to _ 311 * 312 * @param pattern 313 * @return a map of K key to ICacheElement<K, V> element, or an empty map if there is no 314 * data matching the pattern. 315 * @throws IOException 316 */ 317 @Override 318 public Map<K, ICacheElement<K, V>> getMatching( String pattern ) 319 throws IOException 320 { 321 // Get the keys from purgatory 322 Set<K> keyArray = null; 323 324 // this avoids locking purgatory, but it uses more memory 325 synchronized ( purgatory ) 326 { 327 keyArray = new HashSet<K>(purgatory.keySet()); 328 } 329 330 Set<K> matchingKeys = getKeyMatcher().getMatchingKeysFromArray( pattern, keyArray ); 331 332 // call getMultiple with the set 333 Map<K, ICacheElement<K, V>> result = processGetMultiple( matchingKeys ); 334 335 // Get the keys from disk 336 Map<K, ICacheElement<K, V>> diskMatches = doGetMatching( pattern ); 337 338 result.putAll( diskMatches ); 339 340 return result; 341 } 342 343 /** 344 * Gets multiple items from the cache based on the given set of keys. 345 * 346 * @param keys 347 * @return a map of K key to ICacheElement<K, V> element, or an empty map if there is no 348 * data in cache for any of these keys 349 */ 350 @Override 351 public Map<K, ICacheElement<K, V>> processGetMultiple(Set<K> keys) 352 { 353 Map<K, ICacheElement<K, V>> elements = new HashMap<K, ICacheElement<K, V>>(); 354 355 if ( keys != null && !keys.isEmpty() ) 356 { 357 for (K key : keys) 358 { 359 ICacheElement<K, V> element = get( key ); 360 361 if ( element != null ) 362 { 363 elements.put( key, element ); 364 } 365 } 366 } 367 368 return elements; 369 } 370 371 /** 372 * The keys in the cache. 373 * 374 * @see org.apache.commons.jcs.auxiliary.AuxiliaryCache#getKeySet() 375 */ 376 @Override 377 public abstract Set<K> getKeySet() throws IOException; 378 379 /** 380 * Removes are not queued. A call to remove is immediate. 381 * 382 * @param key 383 * @return whether the item was present to be removed. 384 * @throws IOException 385 * @see org.apache.commons.jcs.engine.behavior.ICache#remove 386 */ 387 @Override 388 public final boolean remove( K key ) 389 throws IOException 390 { 391 PurgatoryElement<K, V> pe = null; 392 393 synchronized ( purgatory ) 394 { 395 // I'm getting the object, so I can lock on the element 396 // Remove element from purgatory if it is there 397 pe = purgatory.get( key ); 398 } 399 400 if ( pe != null ) 401 { 402 synchronized ( pe.getCacheElement() ) 403 { 404 synchronized ( purgatory ) 405 { 406 purgatory.remove( key ); 407 } 408 409 // no way to remove from queue, just make sure it doesn't get on 410 // disk and then removed right afterwards 411 pe.setSpoolable( false ); 412 413 // Remove from persistent store immediately 414 doRemove( key ); 415 } 416 } 417 else 418 { 419 // Remove from persistent store immediately 420 doRemove( key ); 421 } 422 423 return false; 424 } 425 426 /** 427 * @throws IOException 428 * @see org.apache.commons.jcs.engine.behavior.ICache#removeAll 429 */ 430 @Override 431 public final void removeAll() 432 throws IOException 433 { 434 if ( this.diskCacheAttributes.isAllowRemoveAll() ) 435 { 436 // Replace purgatory with a new empty hashtable 437 initPurgatory(); 438 439 // Remove all from persistent store immediately 440 doRemoveAll(); 441 } 442 else 443 { 444 if ( log.isInfoEnabled() ) 445 { 446 log.info( "RemoveAll was requested but the request was not fulfilled: allowRemoveAll is set to false." ); 447 } 448 } 449 } 450 451 /** 452 * Adds a dispose request to the disk cache. 453 * 454 * Disposal proceeds in several steps. 455 * <ol> 456 * <li>Prior to this call the Composite cache dumped the memory into the disk cache. If it is 457 * large then we need to wait for the event queue to finish.</li> 458 * <li>Wait until the event queue is empty of until the configured ShutdownSpoolTimeLimit is 459 * reached.</li> 460 * <li>Call doDispose on the concrete impl.</li> 461 * </ol> 462 * @throws IOException 463 */ 464 @Override 465 public final void dispose() 466 throws IOException 467 { 468 Runnable disR = new Runnable() 469 { 470 @Override 471 public void run() 472 { 473 boolean keepGoing = true; 474 // long total = 0; 475 long interval = 100; 476 while ( keepGoing ) 477 { 478 keepGoing = !cacheEventQueue.isEmpty(); 479 try 480 { 481 Thread.sleep( interval ); 482 // total += interval; 483 // log.info( "total = " + total ); 484 } 485 catch ( InterruptedException e ) 486 { 487 break; 488 } 489 } 490 log.info( "No longer waiting for event queue to finish: " + cacheEventQueue.getStatistics() ); 491 } 492 }; 493 Thread t = new Thread( disR ); 494 t.start(); 495 // wait up to 60 seconds for dispose and then quit if not done. 496 try 497 { 498 t.join( this.diskCacheAttributes.getShutdownSpoolTimeLimit() * 1000L ); 499 } 500 catch ( InterruptedException ex ) 501 { 502 log.error( "The Shutdown Spool Process was interrupted.", ex ); 503 } 504 505 log.info( "In dispose, destroying event queue." ); 506 // This stops the processor thread. 507 cacheEventQueue.destroy(); 508 509 // Invoke any implementation specific disposal code 510 // need to handle the disposal first. 511 doDispose(); 512 513 alive = false; 514 } 515 516 /** 517 * @return the region name. 518 * @see ICache#getCacheName 519 */ 520 @Override 521 public String getCacheName() 522 { 523 return cacheName; 524 } 525 526 /** 527 * Gets basic stats for the abstract disk cache. 528 * 529 * @return String 530 */ 531 @Override 532 public String getStats() 533 { 534 return getStatistics().toString(); 535 } 536 537 /** 538 * Returns semi-structured data. 539 * 540 * @see org.apache.commons.jcs.auxiliary.AuxiliaryCache#getStatistics() 541 */ 542 @Override 543 public IStats getStatistics() 544 { 545 IStats stats = new Stats(); 546 stats.setTypeName( "Abstract Disk Cache" ); 547 548 ArrayList<IStatElement<?>> elems = new ArrayList<IStatElement<?>>(); 549 550 elems.add(new StatElement<Integer>( "Purgatory Hits", Integer.valueOf(purgHits) ) ); 551 elems.add(new StatElement<Integer>( "Purgatory Size", Integer.valueOf(purgatory.size()) ) ); 552 553 // get the stats from the event queue too 554 IStats eqStats = this.cacheEventQueue.getStatistics(); 555 elems.addAll(eqStats.getStatElements()); 556 557 stats.setStatElements( elems ); 558 559 return stats; 560 } 561 562 /** 563 * @return the status -- alive or disposed from CacheConstants 564 * @see ICache#getStatus 565 */ 566 @Override 567 public CacheStatus getStatus() 568 { 569 return ( alive ? CacheStatus.ALIVE : CacheStatus.DISPOSED ); 570 } 571 572 /** 573 * Size cannot be determined without knowledge of the cache implementation, so subclasses will 574 * need to implement this method. 575 * 576 * @return the number of items. 577 * @see ICache#getSize 578 */ 579 @Override 580 public abstract int getSize(); 581 582 /** 583 * @see org.apache.commons.jcs.engine.behavior.ICacheType#getCacheType 584 * @return Always returns DISK_CACHE since subclasses should all be of that type. 585 */ 586 @Override 587 public CacheType getCacheType() 588 { 589 return CacheType.DISK_CACHE; 590 } 591 592 /** 593 * Cache that implements the CacheListener interface, and calls appropriate methods in its 594 * parent class. 595 */ 596 protected class MyCacheListener 597 implements ICacheListener<K, V> 598 { 599 /** Id of the listener */ 600 private long listenerId = 0; 601 602 /** 603 * @return cacheElement.getElementAttributes(); 604 * @throws IOException 605 * @see ICacheListener#getListenerId 606 */ 607 @Override 608 public long getListenerId() 609 throws IOException 610 { 611 return this.listenerId; 612 } 613 614 /** 615 * @param id 616 * @throws IOException 617 * @see ICacheListener#setListenerId 618 */ 619 @Override 620 public void setListenerId( long id ) 621 throws IOException 622 { 623 this.listenerId = id; 624 } 625 626 /** 627 * @param element 628 * @throws IOException 629 * @see ICacheListener#handlePut NOTE: This checks if the element is a puratory element and 630 * behaves differently depending. However since we have control over how elements are 631 * added to the cache event queue, that may not be needed ( they are always 632 * PurgatoryElements ). 633 */ 634 @Override 635 public void handlePut( ICacheElement<K, V> element ) 636 throws IOException 637 { 638 if ( alive ) 639 { 640 // If the element is a PurgatoryElement<K, V> we must check to see 641 // if it is still spoolable, and remove it from purgatory. 642 if ( element instanceof PurgatoryElement ) 643 { 644 PurgatoryElement<K, V> pe = (PurgatoryElement<K, V>) element; 645 646 synchronized ( pe.getCacheElement() ) 647 { 648 // TODO consider a timeout. 649 // we need this so that we can have multiple update 650 // threads and still have removeAll requests come in that 651 // always win 652 removeAllLock.readLock().lock(); 653 654 try 655 { 656 // TODO consider changing purgatory sync 657 // String keyAsString = element.getKey().toString(); 658 synchronized ( purgatory ) 659 { 660 // If the element has already been removed from 661 // purgatory do nothing 662 if ( !purgatory.containsKey( pe.getKey() ) ) 663 { 664 return; 665 } 666 667 element = pe.getCacheElement(); 668 } 669 670 // I took this out of the purgatory sync block. 671 // If the element is still eligible, spool it. 672 if ( pe.isSpoolable() ) 673 { 674 doUpdate( element ); 675 } 676 } 677 finally 678 { 679 removeAllLock.readLock().unlock(); 680 } 681 682 synchronized ( purgatory ) 683 { 684 // After the update has completed, it is safe to 685 // remove the element from purgatory. 686 purgatory.remove( element.getKey() ); 687 } 688 } 689 } 690 else 691 { 692 // call the child's implementation 693 doUpdate( element ); 694 } 695 } 696 else 697 { 698 /* 699 * The cache is not alive, hence the element should be removed from purgatory. All 700 * elements should be removed eventually. Perhaps, the alive check should have been 701 * done before it went in the queue. This block handles the case where the disk 702 * cache fails during normal operations. 703 */ 704 synchronized ( purgatory ) 705 { 706 purgatory.remove( element.getKey() ); 707 } 708 } 709 } 710 711 /** 712 * @param cacheName 713 * @param key 714 * @throws IOException 715 * @see ICacheListener#handleRemove 716 */ 717 @Override 718 public void handleRemove( String cacheName, K key ) 719 throws IOException 720 { 721 if ( alive ) 722 { 723 if ( doRemove( key ) ) 724 { 725 log.debug( "Element removed, key: " + key ); 726 } 727 } 728 } 729 730 /** 731 * @param cacheName 732 * @throws IOException 733 * @see ICacheListener#handleRemoveAll 734 */ 735 @Override 736 public void handleRemoveAll( String cacheName ) 737 throws IOException 738 { 739 if ( alive ) 740 { 741 doRemoveAll(); 742 } 743 } 744 745 /** 746 * @param cacheName 747 * @throws IOException 748 * @see ICacheListener#handleDispose 749 */ 750 @Override 751 public void handleDispose( String cacheName ) 752 throws IOException 753 { 754 if ( alive ) 755 { 756 doDispose(); 757 } 758 } 759 } 760 761 /** 762 * Before the event logging layer, the subclasses implemented the do* methods. Now the do* 763 * methods call the *WithEventLogging method on the super. The *WithEventLogging methods call 764 * the abstract process* methods. The children implement the process methods. 765 * 766 * ex. doGet calls getWithEventLogging, which calls processGet 767 */ 768 769 /** 770 * Get a value from the persistent store. 771 * 772 * Before the event logging layer, the subclasses implemented the do* methods. Now the do* 773 * methods call the *EventLogging method on the super. The *WithEventLogging methods call the 774 * abstract process* methods. The children implement the process methods. 775 * 776 * @param key Key to locate value for. 777 * @return An object matching key, or null. 778 * @throws IOException 779 */ 780 protected final ICacheElement<K, V> doGet( K key ) 781 throws IOException 782 { 783 return super.getWithEventLogging( key ); 784 } 785 786 /** 787 * Get a value from the persistent store. 788 * 789 * Before the event logging layer, the subclasses implemented the do* methods. Now the do* 790 * methods call the *EventLogging method on the super. The *WithEventLogging methods call the 791 * abstract process* methods. The children implement the process methods. 792 * 793 * @param pattern Used to match keys. 794 * @return A map of matches.. 795 * @throws IOException 796 */ 797 protected final Map<K, ICacheElement<K, V>> doGetMatching( String pattern ) 798 throws IOException 799 { 800 return super.getMatchingWithEventLogging( pattern ); 801 } 802 803 /** 804 * Add a cache element to the persistent store. 805 * 806 * Before the event logging layer, the subclasses implemented the do* methods. Now the do* 807 * methods call the *EventLogging method on the super. The *WithEventLogging methods call the 808 * abstract process* methods. The children implement the process methods. 809 * 810 * @param cacheElement 811 * @throws IOException 812 */ 813 protected final void doUpdate( ICacheElement<K, V> cacheElement ) 814 throws IOException 815 { 816 super.updateWithEventLogging( cacheElement ); 817 } 818 819 /** 820 * Remove an object from the persistent store if found. 821 * 822 * Before the event logging layer, the subclasses implemented the do* methods. Now the do* 823 * methods call the *EventLogging method on the super. The *WithEventLogging methods call the 824 * abstract process* methods. The children implement the process methods. 825 * 826 * @param key Key of object to remove. 827 * @return whether or no the item was present when removed 828 * @throws IOException 829 */ 830 protected final boolean doRemove( K key ) 831 throws IOException 832 { 833 return super.removeWithEventLogging( key ); 834 } 835 836 /** 837 * Remove all objects from the persistent store. 838 * 839 * Before the event logging layer, the subclasses implemented the do* methods. Now the do* 840 * methods call the *EventLogging method on the super. The *WithEventLogging methods call the 841 * abstract process* methods. The children implement the process methods. 842 * 843 * @throws IOException 844 */ 845 protected final void doRemoveAll() 846 throws IOException 847 { 848 super.removeAllWithEventLogging(); 849 } 850 851 /** 852 * Dispose of the persistent store. Note that disposal of purgatory and setting alive to false 853 * does NOT need to be done by this method. 854 * 855 * Before the event logging layer, the subclasses implemented the do* methods. Now the do* 856 * methods call the *EventLogging method on the super. The *WithEventLogging methods call the 857 * abstract process* methods. The children implement the process methods. 858 * 859 * @throws IOException 860 */ 861 protected final void doDispose() 862 throws IOException 863 { 864 super.disposeWithEventLogging(); 865 } 866 867 /** 868 * Gets the extra info for the event log. 869 * 870 * @return disk location 871 */ 872 @Override 873 public String getEventLoggingExtraInfo() 874 { 875 return getDiskLocation(); 876 } 877 878 /** 879 * This is used by the event logging. 880 * 881 * @return the location of the disk, either path or ip. 882 */ 883 protected abstract String getDiskLocation(); 884 }