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 legal-notices/CDDLv1_0.txt 010 * or http://forgerock.org/license/CDDLv1.0.html. 011 * See the License for the specific language governing permissions 012 * and limitations under the License. 013 * 014 * When distributing Covered Code, include this CDDL HEADER in each 015 * file and include the License file at legal-notices/CDDLv1_0.txt. 016 * If applicable, add the following below this CDDL HEADER, with the 017 * fields enclosed by brackets "[]" replaced with your own identifying 018 * information: 019 * Portions Copyright [yyyy] [name of copyright owner] 020 * 021 * CDDL HEADER END 022 * 023 * 024 * Copyright 2006-2008 Sun Microsystems, Inc. 025 * Portions Copyright 2013-2015 ForgeRock AS. 026 */ 027package org.opends.server.api; 028 029import java.util.HashSet; 030import java.util.List; 031import java.util.Set; 032import java.util.concurrent.atomic.AtomicLong; 033 034import org.forgerock.i18n.LocalizableMessage; 035import org.forgerock.i18n.slf4j.LocalizedLogger; 036import org.forgerock.opendj.config.server.ConfigException; 037import org.opends.server.admin.std.server.EntryCacheCfg; 038import org.opends.server.monitors.EntryCacheMonitorProvider; 039import org.opends.server.types.Attribute; 040import org.opends.server.types.DN; 041import org.opends.server.types.Entry; 042import org.opends.server.types.InitializationException; 043import org.opends.server.types.SearchFilter; 044 045/** 046 * This class defines the set of methods that must be implemented by a 047 * Directory Server entry cache. Note that components accessing the 048 * entry cache must not depend on any particular behavior. For 049 * example, if a call is made to {@code putEntry} to store an entry in 050 * the cache, there is no guarantee that immediately calling 051 * {@code getEntry} will be able to retrieve it. There are several 052 * potential reasons for this, including: 053 * <UL> 054 * <LI>The entry may have been deleted or replaced by another thread 055 * between the {@code putEntry} and {@code getEntry} calls.</LI> 056 * <LI>The entry cache may implement a purging mechanism and the 057 * entry added may have been purged between the 058 * {@code putEntry} and {@code getEntry} calls.</LI> 059 * <LI>The entry cache may implement some kind of filtering 060 * mechanism to determine which entries to store, and entries 061 * not matching the appropriate criteria may not be stored.</LI> 062 * <LI>The entry cache may not actually store any entries (this is 063 * the behavior of the default cache if no implementation 064 * specific entry cache is available).</LI> 065 * </UL> 066 * 067 * @param <T> The type of configuration handled by this entry 068 * cache. 069 */ 070@org.opends.server.types.PublicAPI( 071 stability=org.opends.server.types.StabilityLevel.VOLATILE, 072 mayInstantiate=false, 073 mayExtend=true, 074 mayInvoke=true, 075 notes="Entry cache methods may only be invoked by backends") 076public abstract class EntryCache<T extends EntryCacheCfg> 077{ 078 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 079 080 /** 081 * The set of filters that define the entries that should be 082 * excluded from the cache. 083 */ 084 private Set<SearchFilter> excludeFilters = new HashSet<>(0); 085 086 /** 087 * The set of filters that define the entries that should be 088 * included in the cache. 089 */ 090 private Set<SearchFilter> includeFilters = new HashSet<>(0); 091 092 /** 093 * Arbitrary number of cache hits for monitoring. 094 */ 095 protected AtomicLong cacheHits = new AtomicLong(0); 096 097 /** 098 * Arbitrary number of cache misses for monitoring. 099 */ 100 protected AtomicLong cacheMisses = new AtomicLong(0); 101 102 /** The monitor associated with this entry cache. */ 103 private EntryCacheMonitorProvider entryCacheMonitor; 104 105 106 /** 107 * Default constructor which is implicitly called from all entry 108 * cache implementations. 109 */ 110 public EntryCache() 111 { 112 // No implementation required. 113 } 114 115 /** 116 * Initializes this entry cache implementation so that it will be 117 * available for storing and retrieving entries. 118 * 119 * @param configuration The configuration to use to initialize 120 * the entry cache. 121 * 122 * @throws ConfigException If there is a problem with the provided 123 * configuration entry that would prevent 124 * this entry cache from being used. 125 * 126 * @throws InitializationException If a problem occurs during the 127 * initialization process that is 128 * not related to the 129 * configuration. 130 */ 131 public abstract void initializeEntryCache(T configuration) 132 throws ConfigException, InitializationException; 133 134 /** 135 * Indicates whether the provided configuration is acceptable for 136 * this entry cache. It should be possible to call this method on 137 * an uninitialized entry cache instance in order to determine 138 * whether the entry cache would be able to use the provided 139 * configuration. 140 * <BR><BR> 141 * Note that implementations which use a subclass of the provided 142 * configuration class will likely need to cast the configuration 143 * to the appropriate subclass type. 144 * 145 * @param configuration The entry cache configuration for 146 * which to make the determination. 147 * @param unacceptableReasons A list that may be used to hold the 148 * reasons that the provided 149 * configuration is not acceptable. 150 * 151 * @return {@code true} if the provided configuration is acceptable 152 * for this entry cache, or {@code false} if not. 153 */ 154 public boolean isConfigurationAcceptable( 155 EntryCacheCfg configuration, 156 List<LocalizableMessage> unacceptableReasons) 157 { 158 // This default implementation does not perform any special 159 // validation. It should be overridden by entry cache 160 // implementations that wish to perform more detailed validation. 161 return true; 162 } 163 164 /** 165 * Performs any necessary cleanup work (e.g., flushing all cached 166 * entries and releasing any other held resources) that should be 167 * performed when the server is to be shut down or the entry cache 168 * destroyed or replaced. 169 */ 170 public abstract void finalizeEntryCache(); 171 172 /** 173 * Indicates whether the entry cache currently contains the entry 174 * with the specified DN. This method may be called without holding 175 * any locks if a point-in-time check is all that is required. 176 * Note that this method is called from @see #getEntry(DN entryDN, 177 * LockType lockType, List lockList) 178 * 179 * @param entryDN The DN for which to make the determination. 180 * 181 * @return {@code true} if the entry cache currently contains the 182 * entry with the specified DN, or {@code false} if not. 183 */ 184 public abstract boolean containsEntry(DN entryDN); 185 186 /** 187 * Retrieves the entry with the specified DN from the cache. 188 * 189 * @param entryDN The DN of the entry to retrieve. 190 * 191 * @return The requested entry if it is present in the cache, or 192 * {@code null} if it is not present. 193 */ 194 public abstract Entry getEntry(DN entryDN); 195 196 /** 197 * Retrieves the requested entry if it is present in the cache. 198 * 199 * @param backendID ID of the backend associated with the entry 200 * to retrieve. 201 * @param entryID The entry ID within the provided backend for 202 * the specified entry. 203 * 204 * @return The requested entry if it is present in the cache, or 205 * {@code null} if it is not present. 206 */ 207 public Entry getEntry(String backendID, long entryID) 208 { 209 // Translate given backend/entryID pair to entryDN. 210 DN entryDN = getEntryDN(backendID, entryID); 211 if (entryDN == null) 212 { 213 // Indicate cache miss. 214 cacheMisses.getAndIncrement(); 215 return null; 216 } 217 // Delegate to by DN lock and load method. 218 return getEntry(entryDN); 219 } 220 221 /** 222 * Retrieves the entry ID for the entry with the specified DN from 223 * the cache. The caller should have already acquired a read or 224 * write lock for the entry if such protection is needed. 225 * 226 * @param entryDN The DN of the entry for which to retrieve the 227 * entry ID. 228 * 229 * @return The entry ID for the requested entry, or -1 if it is 230 * not present in the cache. 231 */ 232 public abstract long getEntryID(DN entryDN); 233 234 /** 235 * Retrieves the entry DN for the entry with the specified ID on 236 * the specific backend from the cache. The caller should have 237 * already acquired a read or write lock for the entry if such 238 * protection is needed. 239 * Note that this method is called from @see #getEntry(Backend 240 * backend, long entryID, LockType lockType, List lockList) 241 * 242 * @param backendID ID of the backend associated with the 243 * entry for which to retrieve the entry DN. 244 * @param entryID The entry ID within the provided backend 245 * for which to retrieve the entry DN. 246 * 247 * @return The entry DN for the requested entry, or 248 * {@code null} if it is not present in the cache. 249 */ 250 public abstract DN getEntryDN(String backendID, long entryID); 251 252 /** 253 * Stores the provided entry in the cache. Note that the mechanism 254 * that it uses to achieve this is implementation-dependent, and it 255 * is acceptable for the entry to not actually be stored in any 256 * cache. 257 * 258 * @param entry The entry to store in the cache. 259 * @param backendID ID of the backend with which the entry is 260 * associated. 261 * @param entryID The entry ID within the provided backend that 262 * uniquely identifies the specified entry. 263 */ 264 public abstract void putEntry(Entry entry, String backendID, long entryID); 265 266 /** 267 * Stores the provided entry in the cache only if it does not 268 * conflict with an entry that already exists. Note that the 269 * mechanism that it uses to achieve this is 270 * implementation-dependent, and it is acceptable for the entry to 271 * not actually be stored in any cache. However, this method must 272 * not overwrite an existing version of the entry. 273 * 274 * @param entry The entry to store in the cache. 275 * @param backendID ID of the backend with which the entry is 276 * associated. 277 * @param entryID The entry ID within the provided backend that 278 * uniquely identifies the specified entry. 279 * 280 * @return {@code false} if an existing entry or some other problem 281 * prevented the method from completing successfully, or 282 * {@code true} if there was no conflict and the entry was 283 * either stored or the cache determined that this entry 284 * should never be cached for some reason. 285 */ 286 public abstract boolean putEntryIfAbsent(Entry entry, 287 String backendID, 288 long entryID); 289 290 /** 291 * Removes the specified entry from the cache. 292 * 293 * @param entryDN The DN of the entry to remove from the cache. 294 */ 295 public abstract void removeEntry(DN entryDN); 296 297 /** 298 * Removes all entries from the cache. The cache should still be 299 * available for future use. 300 */ 301 public abstract void clear(); 302 303 /** 304 * Removes all entries from the cache that are associated with the 305 * provided backend. 306 * 307 * @param backendID ID of the backend for which to flush the 308 * associated entries. 309 */ 310 public abstract void clearBackend(String backendID); 311 312 /** 313 * Removes all entries from the cache that are below the provided 314 * DN. 315 * 316 * @param baseDN The base DN below which all entries should be 317 * flushed. 318 */ 319 public abstract void clearSubtree(DN baseDN); 320 321 /** 322 * Attempts to react to a scenario in which it is determined that 323 * the system is running low on available memory. In this case, the 324 * entry cache should attempt to free some memory if possible to try 325 * to avoid out of memory errors. 326 */ 327 public abstract void handleLowMemory(); 328 329 /** 330 * Retrieves the monitor that is associated with this entry 331 * cache. 332 * 333 * @return The monitor that is associated with this entry 334 * cache, or {@code null} if none has been assigned. 335 */ 336 public final EntryCacheMonitorProvider getEntryCacheMonitor() 337 { 338 return entryCacheMonitor; 339 } 340 341 /** 342 * Sets the monitor for this entry cache. 343 * 344 * @param entryCacheMonitor The monitor for this entry cache. 345 */ 346 public final void setEntryCacheMonitor( 347 EntryCacheMonitorProvider entryCacheMonitor) 348 { 349 this.entryCacheMonitor = entryCacheMonitor; 350 } 351 352 /** 353 * Retrieves a set of attributes containing monitor data that should 354 * be returned to the client if the corresponding monitor entry is 355 * requested. 356 * 357 * @return A list of attributes containing monitor data that should 358 * be returned to the client if the corresponding monitor 359 * entry is requested. 360 */ 361 public abstract List<Attribute> getMonitorData(); 362 363 /** 364 * Retrieves the current number of entries stored within the cache. 365 * 366 * @return The current number of entries stored within the cache. 367 */ 368 public abstract Long getCacheCount(); 369 370 /** 371 * Retrieves the current number of cache hits for this cache. 372 * 373 * @return The current number of cache hits for this cache. 374 */ 375 public long getCacheHits() 376 { 377 return cacheHits.longValue(); 378 } 379 380 /** 381 * Retrieves the current number of cache misses for this cache. 382 * 383 * @return The current number of cache misses for this cache. 384 */ 385 public long getCacheMisses() 386 { 387 return cacheMisses.longValue(); 388 } 389 390 /** 391 * Retrieves the set of search filters that may be used to determine 392 * whether an entry should be excluded from the cache. 393 * 394 * @return The set of search filters that may be used to determine 395 * whether an entry should be excluded from the cache. 396 */ 397 public Set<SearchFilter> getExcludeFilters() 398 { 399 return excludeFilters; 400 } 401 402 /** 403 * Specifies the set of search filters that may be used to determine 404 * whether an entry should be excluded from the cache. 405 * 406 * @param excludeFilters The set of search filters that may be 407 * used to determine whether an entry should 408 * be excluded from the cache. 409 */ 410 public void setExcludeFilters(Set<SearchFilter> excludeFilters) 411 { 412 if (excludeFilters == null) 413 { 414 this.excludeFilters = new HashSet<>(0); 415 } 416 else 417 { 418 this.excludeFilters = excludeFilters; 419 } 420 } 421 422 /** 423 * Retrieves the set of search filters that may be used to determine 424 * whether an entry should be included in the cache. 425 * 426 * @return The set of search filters that may be used to determine 427 * whether an entry should be included in the cache. 428 */ 429 public Set<SearchFilter> getIncludeFilters() 430 { 431 return includeFilters; 432 } 433 434 /** 435 * Specifies the set of search filters that may be used to determine 436 * whether an entry should be included in the cache. 437 * 438 * @param includeFilters The set of search filters that may be 439 * used to determine whether an entry should 440 * be included in the cache. 441 */ 442 public void setIncludeFilters(Set<SearchFilter> includeFilters) 443 { 444 if (includeFilters == null) 445 { 446 this.includeFilters = new HashSet<>(0); 447 } 448 else 449 { 450 this.includeFilters = includeFilters; 451 } 452 } 453 454 /** 455 * Indicates whether the current set of exclude and include filters 456 * allow caching of the specified entry. 457 * 458 * @param entry The entry to evaluate against exclude and include 459 * filter sets. 460 * 461 * @return {@code true} if current set of filters allow caching the 462 * entry and {@code false} otherwise. 463 */ 464 public boolean filtersAllowCaching(Entry entry) 465 { 466 // If there is a set of exclude filters, then make sure that the 467 // provided entry doesn't match any of them. 468 if (! excludeFilters.isEmpty()) 469 { 470 for (SearchFilter f : excludeFilters) 471 { 472 try 473 { 474 if (f.matchesEntry(entry)) 475 { 476 return false; 477 } 478 } 479 catch (Exception e) 480 { 481 logger.traceException(e); 482 483 // This shouldn't happen, but if it does then we can't be 484 // sure whether the entry should be excluded, so we will 485 // by default. 486 return false; 487 } 488 } 489 } 490 491 // If there is a set of include filters, then make sure that the 492 // provided entry matches at least one of them. 493 if (! includeFilters.isEmpty()) 494 { 495 boolean matchFound = false; 496 for (SearchFilter f : includeFilters) 497 { 498 try 499 { 500 if (f.matchesEntry(entry)) 501 { 502 matchFound = true; 503 break; 504 } 505 } 506 catch (Exception e) 507 { 508 logger.traceException(e); 509 510 // This shouldn't happen, but if it does, then 511 // just ignore it. 512 } 513 } 514 515 if (! matchFound) 516 { 517 return false; 518 } 519 } 520 521 return true; 522 } 523 524 /** 525 * Return a verbose string representation of the current cache maps. This is 526 * useful primary for debugging and diagnostic purposes such as in the entry 527 * cache unit tests. 528 * <p> 529 * This method is invoked by unit tests for debugging. 530 * 531 * @return String verbose string representation of the current cache maps in 532 * the following format: dn:id:backend one cache entry map 533 * representation per line or <CODE>null</CODE> if all maps are empty. 534 */ 535 public abstract String toVerboseString(); 536}