001 /*
002 * CDDL HEADER START
003 *
004 * The contents of this file are subject to the terms of the
005 * Common Development and Distribution License, Version 1.0 only
006 * (the "License"). You may not use this file except in compliance
007 * with the License.
008 *
009 * You can obtain a copy of the license at
010 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
011 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
012 * See the License for the specific language governing permissions
013 * and limitations under the License.
014 *
015 * When distributing Covered Code, include this CDDL HEADER in each
016 * file and include the License file at
017 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
018 * add the following below this CDDL HEADER, with the fields enclosed
019 * by brackets "[]" replaced with your own identifying information:
020 * Portions Copyright [yyyy] [name of copyright owner]
021 *
022 * CDDL HEADER END
023 *
024 *
025 * Copyright 2006-2008 Sun Microsystems, Inc.
026 */
027 package org.opends.server.api;
028 import org.opends.messages.Message;
029
030
031
032 import java.util.List;
033 import java.util.ArrayList;
034 import java.util.HashSet;
035 import java.util.Set;
036 import java.util.concurrent.locks.Lock;
037 import java.util.concurrent.atomic.AtomicLong;
038
039 import org.opends.server.config.ConfigException;
040 import org.opends.server.types.DN;
041 import org.opends.server.types.Entry;
042 import org.opends.server.types.InitializationException;
043 import org.opends.server.types.LockType;
044 import org.opends.server.types.LockManager;
045 import org.opends.server.types.SearchFilter;
046 import org.opends.server.types.DebugLogLevel;
047 import org.opends.server.admin.std.server.EntryCacheCfg;
048 import org.opends.server.loggers.debug.DebugTracer;
049 import org.opends.server.monitors.EntryCacheMonitorProvider;
050 import org.opends.server.types.Attribute;
051 import static org.opends.server.loggers.debug.DebugLogger.*;
052
053
054
055 /**
056 * This class defines the set of methods that must be implemented by a
057 * Directory Server entry cache. Note that components accessing the
058 * entry cache must not depend on any particular behavior. For
059 * example, if a call is made to {@code putEntry} to store an entry in
060 * the cache, there is no guarantee that immediately calling
061 * {@code getEntry} will be able to retrieve it. There are several
062 * potential reasons for this, including:
063 * <UL>
064 * <LI>The entry may have been deleted or replaced by another thread
065 * between the {@code putEntry} and {@code getEntry} calls.</LI>
066 * <LI>The entry cache may implement a purging mechanism and the
067 * entry added may have been purged between the
068 * {@code putEntry} and {@code getEntry} calls.</LI>
069 * <LI>The entry cache may implement some kind of filtering
070 * mechanism to determine which entries to store, and entries
071 * not matching the appropriate criteria may not be stored.</LI>
072 * <LI>The entry cache may not actually store any entries (this is
073 * the behavior of the default cache if no implementation
074 * specific entry cache is available).</LI>
075 * </UL>
076 *
077 * @param <T> The type of configuration handled by this entry
078 * cache.
079 */
080 @org.opends.server.types.PublicAPI(
081 stability=org.opends.server.types.StabilityLevel.VOLATILE,
082 mayInstantiate=false,
083 mayExtend=true,
084 mayInvoke=true,
085 notes="Entry cache methods may only be invoked by backends")
086 public abstract class EntryCache
087 <T extends EntryCacheCfg>
088 {
089 /**
090 * The tracer object for the debug logger.
091 */
092 private static final DebugTracer TRACER = getTracer();
093
094 // The set of filters that define the entries that should be
095 // excluded from the cache.
096 private Set<SearchFilter> excludeFilters =
097 new HashSet<SearchFilter>(0);
098
099 // The set of filters that define the entries that should be
100 // included in the cache.
101 private Set<SearchFilter> includeFilters =
102 new HashSet<SearchFilter>(0);
103
104 // The maximum length of time to try to obtain a lock before giving
105 // up.
106 private long lockTimeout = LockManager.DEFAULT_TIMEOUT;
107
108 /**
109 * Arbitrary number of cache hits for monitoring.
110 */
111 protected AtomicLong cacheHits = new AtomicLong(0);
112
113 /**
114 * Arbitrary number of cache misses for monitoring.
115 */
116 protected AtomicLong cacheMisses = new AtomicLong(0);
117
118 // The monitor associated with this entry cache.
119 private EntryCacheMonitorProvider entryCacheMonitor = null;
120
121
122 /**
123 * Default constructor which is implicitly called from all entry
124 * cache implementations.
125 */
126 public EntryCache()
127 {
128 // No implementation required.
129 }
130
131
132
133 /**
134 * Initializes this entry cache implementation so that it will be
135 * available for storing and retrieving entries.
136 *
137 * @param configuration The configuration to use to initialize
138 * the entry cache.
139 *
140 * @throws ConfigException If there is a problem with the provided
141 * configuration entry that would prevent
142 * this entry cache from being used.
143 *
144 * @throws InitializationException If a problem occurs during the
145 * initialization process that is
146 * not related to the
147 * configuration.
148 */
149 public abstract void initializeEntryCache(T configuration)
150 throws ConfigException, InitializationException;
151
152
153
154 /**
155 * Indicates whether the provided configuration is acceptable for
156 * this entry cache. It should be possible to call this method on
157 * an uninitialized entry cache instance in order to determine
158 * whether the entry cache would be able to use the provided
159 * configuration.
160 * <BR><BR>
161 * Note that implementations which use a subclass of the provided
162 * configuration class will likely need to cast the configuration
163 * to the appropriate subclass type.
164 *
165 * @param configuration The entry cache configuration for
166 * which to make the determination.
167 * @param unacceptableReasons A list that may be used to hold the
168 * reasons that the provided
169 * configuration is not acceptable.
170 *
171 * @return {@code true} if the provided configuration is acceptable
172 * for this entry cache, or {@code false} if not.
173 */
174 public boolean isConfigurationAcceptable(
175 EntryCacheCfg configuration,
176 List<Message> unacceptableReasons)
177 {
178 // This default implementation does not perform any special
179 // validation. It should be overridden by entry cache
180 // implementations that wish to perform more detailed validation.
181 return true;
182 }
183
184
185
186 /**
187 * Performs any necessary cleanup work (e.g., flushing all cached
188 * entries and releasing any other held resources) that should be
189 * performed when the server is to be shut down or the entry cache
190 * destroyed or replaced.
191 */
192 public abstract void finalizeEntryCache();
193
194
195
196 /**
197 * Indicates whether the entry cache currently contains the entry
198 * with the specified DN. This method may be called without holding
199 * any locks if a point-in-time check is all that is required.
200 * Note that this method is called from @see #getEntry(DN entryDN,
201 * LockType lockType, List lockList)
202 *
203 * @param entryDN The DN for which to make the determination.
204 *
205 * @return {@code true} if the entry cache currently contains the
206 * entry with the specified DN, or {@code false} if not.
207 */
208 public abstract boolean containsEntry(DN entryDN);
209
210
211
212 /**
213 * Retrieves the entry with the specified DN from the cache. The
214 * caller should have already acquired a read or write lock for the
215 * entry if such protection is needed.
216 * Note that this method is called from @see #getEntry(DN entryDN,
217 * LockType lockType, List lockList)
218 *
219 * @param entryDN The DN of the entry to retrieve.
220 *
221 * @return The requested entry if it is present in the cache, or
222 * {@code null} if it is not present.
223 */
224 public abstract Entry getEntry(DN entryDN);
225
226
227
228 /**
229 * Retrieves the entry with the specified DN from the cache,
230 * obtaining a lock on the entry before it is returned. If the
231 * entry is present in the cache, then a lock will be obtained for
232 * that entry and appended to the provided list before the entry is
233 * returned. If the entry is not present, then no lock will be
234 * obtained. Note that although this method is declared non-final
235 * it is not recommended for subclasses to implement this method.
236 *
237 * @param entryDN The DN of the entry to retrieve.
238 * @param lockType The type of lock to obtain (it may be
239 * {@code NONE}).
240 * @param lockList The list to which the obtained lock will be
241 * added (note that no lock will be added if the
242 * lock type was {@code NONE}).
243 *
244 * @return The requested entry if it is present in the cache, or
245 * {@code null} if it is not present.
246 */
247 public Entry getEntry(DN entryDN,
248 LockType lockType,
249 List<Lock> lockList) {
250
251 if (!containsEntry(entryDN)) {
252 // Indicate cache miss.
253 cacheMisses.getAndIncrement();
254
255 return null;
256 }
257
258 // Obtain a lock for the entry before actually retrieving the
259 // entry itself thus preventing any stale entries being returned,
260 // see Issue #1589 for more details. If an error occurs, then
261 // make sure no lock is held and return null. Otherwise, return
262 // the entry.
263 switch (lockType)
264 {
265 case READ:
266 // Try to obtain a read lock for the entry.
267 Lock readLock = LockManager.lockRead(entryDN, lockTimeout);
268 if (readLock == null)
269 {
270 // We couldn't get the lock, so we have to return null.
271 return null;
272 }
273 else
274 {
275 try
276 {
277 lockList.add(readLock);
278 // and load.
279 Entry entry = getEntry(entryDN);
280 if (entry == null)
281 {
282 lockList.remove(readLock);
283 LockManager.unlock(entryDN, readLock);
284 return null;
285 }
286 return entry;
287 }
288 catch (Exception e)
289 {
290 if (debugEnabled())
291 {
292 TRACER.debugCaught(DebugLogLevel.ERROR, e);
293 }
294
295 // The attempt to add the lock to the list failed,
296 // so we need to release the lock and return null.
297 try
298 {
299 LockManager.unlock(entryDN, readLock);
300 }
301 catch (Exception e2)
302 {
303 if (debugEnabled())
304 {
305 TRACER.debugCaught(DebugLogLevel.ERROR, e2);
306 }
307 }
308
309 return null;
310 }
311 }
312
313 case WRITE:
314 // Try to obtain a write lock for the entry.
315 Lock writeLock = LockManager.lockWrite(entryDN, lockTimeout);
316 if (writeLock == null)
317 {
318 // We couldn't get the lock, so we have to return null.
319 return null;
320 }
321 else
322 {
323 try
324 {
325 lockList.add(writeLock);
326 // and load.
327 Entry entry = getEntry(entryDN);
328 if (entry == null)
329 {
330 lockList.remove(writeLock);
331 LockManager.unlock(entryDN, writeLock);
332 return null;
333 }
334 return entry;
335 }
336 catch (Exception e)
337 {
338 if (debugEnabled())
339 {
340 TRACER.debugCaught(DebugLogLevel.ERROR, e);
341 }
342
343 // The attempt to add the lock to the list failed,
344 // so we need to release the lock and return null.
345 try
346 {
347 LockManager.unlock(entryDN, writeLock);
348 }
349 catch (Exception e2)
350 {
351 if (debugEnabled())
352 {
353 TRACER.debugCaught(DebugLogLevel.ERROR, e2);
354 }
355 }
356
357 return null;
358 }
359 }
360
361 case NONE:
362 // We don't need to obtain a lock, so just return the entry.
363 Entry entry = getEntry(entryDN);
364 if (entry == null)
365 {
366 return null;
367 }
368 return entry;
369
370 default:
371 // This is an unknown type of lock, so we'll return null.
372 return null;
373 }
374 }
375
376
377
378 /**
379 * Retrieves the requested entry if it is present in the cache,
380 * obtaining a lock on the entry before it is returned. If the
381 * entry is present in the cache, then a lock will be obtained for
382 * that entry and appended to the provided list before the entry is
383 * returned. If the entry is not present, then no lock will be
384 * obtained. Note that although this method is declared non-final
385 * it is not recommended for subclasses to implement this method.
386 *
387 * @param backend The backend associated with the entry to
388 * retrieve.
389 * @param entryID The entry ID within the provided backend for
390 * the specified entry.
391 * @param lockType The type of lock to obtain (it may be
392 * {@code NONE}).
393 * @param lockList The list to which the obtained lock will be
394 * added (note that no lock will be added if the
395 * lock type was {@code NONE}).
396 *
397 * @return The requested entry if it is present in the cache, or
398 * {@code null} if it is not present.
399 */
400 public Entry getEntry(Backend backend, long entryID,
401 LockType lockType,
402 List<Lock> lockList) {
403
404 // Translate given backend/entryID pair to entryDN.
405 DN entryDN = getEntryDN(backend, entryID);
406 if (entryDN == null) {
407 // Indicate cache miss.
408 cacheMisses.getAndIncrement();
409
410 return null;
411 }
412
413 // Delegate to by DN lock and load method.
414 return getEntry(entryDN, lockType, lockList);
415 }
416
417
418
419 /**
420 * Retrieves the entry ID for the entry with the specified DN from
421 * the cache. The caller should have already acquired a read or
422 * write lock for the entry if such protection is needed.
423 *
424 * @param entryDN The DN of the entry for which to retrieve the
425 * entry ID.
426 *
427 * @return The entry ID for the requested entry, or -1 if it is
428 * not present in the cache.
429 */
430 public abstract long getEntryID(DN entryDN);
431
432
433
434 /**
435 * Retrieves the entry DN for the entry with the specified ID on
436 * the specific backend from the cache. The caller should have
437 * already acquired a read or write lock for the entry if such
438 * protection is needed.
439 * Note that this method is called from @see #getEntry(Backend
440 * backend, long entryID, LockType lockType, List lockList)
441 *
442 * @param backend The backend associated with the entry for
443 * which to retrieve the entry DN.
444 * @param entryID The entry ID within the provided backend
445 * for which to retrieve the entry DN.
446 *
447 * @return The entry DN for the requested entry, or
448 * {@code null} if it is not present in the cache.
449 */
450 public abstract DN getEntryDN(Backend backend, long entryID);
451
452
453
454 /**
455 * Stores the provided entry in the cache. Note that the mechanism
456 * that it uses to achieve this is implementation-dependent, and it
457 * is acceptable for the entry to not actually be stored in any
458 * cache.
459 *
460 * @param entry The entry to store in the cache.
461 * @param backend The backend with which the entry is associated.
462 * @param entryID The entry ID within the provided backend that
463 * uniquely identifies the specified entry.
464 */
465 public abstract void putEntry(Entry entry, Backend backend,
466 long entryID);
467
468
469
470 /**
471 * Stores the provided entry in the cache only if it does not
472 * conflict with an entry that already exists. Note that the
473 * mechanism that it uses to achieve this is
474 * implementation-dependent, and it is acceptable for the entry to
475 * not actually be stored in any cache. However, this method must
476 * not overwrite an existing version of the entry.
477 *
478 * @param entry The entry to store in the cache.
479 * @param backend The backend with which the entry is associated.
480 * @param entryID The entry ID within the provided backend that
481 * uniquely identifies the specified entry.
482 *
483 * @return {@code false} if an existing entry or some other problem
484 * prevented the method from completing successfully, or
485 * {@code true} if there was no conflict and the entry was
486 * either stored or the cache determined that this entry
487 * should never be cached for some reason.
488 */
489 public abstract boolean putEntryIfAbsent(Entry entry,
490 Backend backend,
491 long entryID);
492
493
494
495 /**
496 * Removes the specified entry from the cache.
497 *
498 * @param entryDN The DN of the entry to remove from the cache.
499 */
500 public abstract void removeEntry(DN entryDN);
501
502
503
504 /**
505 * Removes all entries from the cache. The cache should still be
506 * available for future use.
507 */
508 public abstract void clear();
509
510
511
512 /**
513 * Removes all entries from the cache that are associated with the
514 * provided backend.
515 *
516 * @param backend The backend for which to flush the associated
517 * entries.
518 */
519 public abstract void clearBackend(Backend backend);
520
521
522
523 /**
524 * Removes all entries from the cache that are below the provided
525 * DN.
526 *
527 * @param baseDN The base DN below which all entries should be
528 * flushed.
529 */
530 public abstract void clearSubtree(DN baseDN);
531
532
533
534 /**
535 * Attempts to react to a scenario in which it is determined that
536 * the system is running low on available memory. In this case, the
537 * entry cache should attempt to free some memory if possible to try
538 * to avoid out of memory errors.
539 */
540 public abstract void handleLowMemory();
541
542
543
544 /**
545 * Retrieves the monitor that is associated with this entry
546 * cache.
547 *
548 * @return The monitor that is associated with this entry
549 * cache, or {@code null} if none has been assigned.
550 */
551 public final EntryCacheMonitorProvider getEntryCacheMonitor()
552 {
553 return entryCacheMonitor;
554 }
555
556
557
558 /**
559 * Sets the monitor for this entry cache.
560 *
561 * @param entryCacheMonitor The monitor for this entry cache.
562 */
563 public final void setEntryCacheMonitor(
564 EntryCacheMonitorProvider entryCacheMonitor)
565 {
566 this.entryCacheMonitor = entryCacheMonitor;
567 }
568
569
570
571 /**
572 * Retrieves a set of attributes containing monitor data that should
573 * be returned to the client if the corresponding monitor entry is
574 * requested.
575 *
576 * @return A set of attributes containing monitor data that should
577 * be returned to the client if the corresponding monitor
578 * entry is requested.
579 */
580 public abstract ArrayList<Attribute> getMonitorData();
581
582
583
584 /**
585 * Retrieves the current number of entries stored within the cache.
586 *
587 * @return The current number of entries stored within the cache.
588 */
589 public abstract Long getCacheCount();
590
591
592
593 /**
594 * Retrieves the current number of cache hits for this cache.
595 *
596 * @return The current number of cache hits for this cache.
597 */
598 public Long getCacheHits()
599 {
600 return new Long(cacheHits.longValue());
601 }
602
603
604
605 /**
606 * Retrieves the current number of cache misses for this cache.
607 *
608 * @return The current number of cache misses for this cache.
609 */
610 public Long getCacheMisses()
611 {
612 return new Long(cacheMisses.longValue());
613 }
614
615
616
617 /**
618 * Retrieves the maximum length of time in milliseconds to wait for
619 * a lock before giving up.
620 *
621 * @return The maximum length of time in milliseconds to wait for a
622 * lock before giving up.
623 */
624 public long getLockTimeout()
625 {
626 return lockTimeout;
627 }
628
629
630
631 /**
632 * Specifies the maximum length of time in milliseconds to wait for
633 * a lock before giving up.
634 *
635 * @param lockTimeout The maximum length of time in milliseconds
636 * to wait for a lock before giving up.
637 */
638 public void setLockTimeout(long lockTimeout)
639 {
640 this.lockTimeout = lockTimeout;
641 }
642
643
644
645 /**
646 * Retrieves the set of search filters that may be used to determine
647 * whether an entry should be excluded from the cache.
648 *
649 * @return The set of search filters that may be used to determine
650 * whether an entry should be excluded from the cache.
651 */
652 public Set<SearchFilter> getExcludeFilters()
653 {
654 return excludeFilters;
655 }
656
657
658
659 /**
660 * Specifies the set of search filters that may be used to determine
661 * whether an entry should be excluded from the cache.
662 *
663 * @param excludeFilters The set of search filters that may be
664 * used to determine whether an entry should
665 * be excluded from the cache.
666 */
667 public void setExcludeFilters(Set<SearchFilter> excludeFilters)
668 {
669 if (excludeFilters == null)
670 {
671 this.excludeFilters = new HashSet<SearchFilter>(0);
672 }
673 else
674 {
675 this.excludeFilters = excludeFilters;
676 }
677 }
678
679
680
681 /**
682 * Retrieves the set of search filters that may be used to determine
683 * whether an entry should be included in the cache.
684 *
685 * @return The set of search filters that may be used to determine
686 * whether an entry should be included in the cache.
687 */
688 public Set<SearchFilter> getIncludeFilters()
689 {
690 return includeFilters;
691 }
692
693
694
695 /**
696 * Specifies the set of search filters that may be used to determine
697 * whether an entry should be included in the cache.
698 *
699 * @param includeFilters The set of search filters that may be
700 * used to determine whether an entry should
701 * be included in the cache.
702 */
703 public void setIncludeFilters(Set<SearchFilter> includeFilters)
704 {
705 if (includeFilters == null)
706 {
707 this.includeFilters = new HashSet<SearchFilter>(0);
708 }
709 else
710 {
711 this.includeFilters = includeFilters;
712 }
713 }
714
715
716
717 /**
718 * Indicates whether the current set of exclude and include filters
719 * allow caching of the specified entry.
720 *
721 * @param entry The entry to evaluate against exclude and include
722 * filter sets.
723 *
724 * @return {@code true} if current set of filters allow caching the
725 * entry and {@code false} otherwise.
726 */
727 public boolean filtersAllowCaching(Entry entry)
728 {
729 // If there is a set of exclude filters, then make sure that the
730 // provided entry doesn't match any of them.
731 if (! excludeFilters.isEmpty())
732 {
733 for (SearchFilter f : excludeFilters)
734 {
735 try
736 {
737 if (f.matchesEntry(entry))
738 {
739 return false;
740 }
741 }
742 catch (Exception e)
743 {
744 if (debugEnabled())
745 {
746 TRACER.debugCaught(DebugLogLevel.ERROR, e);
747 }
748
749 // This shouldn't happen, but if it does then we can't be
750 // sure whether the entry should be excluded, so we will
751 // by default.
752 return false;
753 }
754 }
755 }
756
757 // If there is a set of include filters, then make sure that the
758 // provided entry matches at least one of them.
759 if (! includeFilters.isEmpty())
760 {
761 boolean matchFound = false;
762 for (SearchFilter f : includeFilters)
763 {
764 try
765 {
766 if (f.matchesEntry(entry))
767 {
768 matchFound = true;
769 break;
770 }
771 }
772 catch (Exception e)
773 {
774 if (debugEnabled())
775 {
776 TRACER.debugCaught(DebugLogLevel.ERROR, e);
777 }
778
779 // This shouldn't happen, but if it does, then
780 // just ignore it.
781 }
782 }
783
784 if (! matchFound)
785 {
786 return false;
787 }
788 }
789
790 return true;
791 }
792 }