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 }