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