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