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 2008 Sun Microsystems, Inc. 026 */ 027 package org.opends.server.extensions; 028 import java.lang.reflect.Method; 029 import org.opends.messages.Message; 030 031 032 033 import java.util.ArrayList; 034 import java.util.List; 035 import java.util.SortedMap; 036 import java.util.concurrent.locks.Lock; 037 038 import org.opends.server.admin.server.ConfigurationChangeListener; 039 import org.opends.server.admin.std.server.EntryCacheCfg; 040 import org.opends.server.api.Backend; 041 import org.opends.server.api.BackendInitializationListener; 042 import org.opends.server.api.EntryCache; 043 import org.opends.server.config.ConfigException; 044 import org.opends.server.core.DirectoryServer; 045 import org.opends.server.loggers.debug.DebugTracer; 046 import org.opends.server.types.Attribute; 047 import org.opends.server.types.ConfigChangeResult; 048 import org.opends.server.types.DN; 049 import org.opends.server.types.DebugLogLevel; 050 import org.opends.server.types.Entry; 051 import org.opends.server.types.InitializationException; 052 import org.opends.server.types.LockType; 053 import org.opends.server.types.ResultCode; 054 055 import static org.opends.server.loggers.debug.DebugLogger.*; 056 057 /** 058 * This class defines the default entry cache which acts as an arbiter for 059 * every entry cache implementation configured and installed within the 060 * Directory Server or acts an an empty cache if no implementation specific 061 * entry cache is configured. It does not actually store any entries, so 062 * all calls to the entry cache public API are routed to underlying entry 063 * cache according to the current configuration order and preferences. 064 */ 065 public class DefaultEntryCache 066 extends EntryCache<EntryCacheCfg> 067 implements ConfigurationChangeListener<EntryCacheCfg>, 068 BackendInitializationListener 069 { 070 /** 071 * The tracer object for the debug logger. 072 */ 073 private static final DebugTracer TRACER = getTracer(); 074 075 076 // The entry cache order array reflects all currently configured and 077 // active entry cache implementations in cache level specific order. 078 private static EntryCache<? extends EntryCacheCfg>[] cacheOrder = 079 new EntryCache<?>[0]; 080 081 082 /** 083 * Creates a new instance of this default entry cache. 084 */ 085 public DefaultEntryCache() 086 { 087 super(); 088 089 // Register with backend initialization listener to clear cache 090 // entries belonging to given backend that about to go offline. 091 DirectoryServer.registerBackendInitializationListener(this); 092 } 093 094 095 /** 096 * {@inheritDoc} 097 */ 098 public void initializeEntryCache(EntryCacheCfg configEntry) 099 throws ConfigException, InitializationException 100 { 101 // No implementation required. 102 } 103 104 105 /** 106 * {@inheritDoc} 107 */ 108 public void finalizeEntryCache() 109 { 110 for (EntryCache entryCache : cacheOrder) { 111 entryCache.finalizeEntryCache(); 112 } 113 // ReInitialize cache order array. 114 cacheOrder = new EntryCache<?>[0]; 115 } 116 117 118 /** 119 * {@inheritDoc} 120 */ 121 public boolean containsEntry(DN entryDN) 122 { 123 if (entryDN == null) { 124 return false; 125 } 126 127 for (EntryCache entryCache : cacheOrder) { 128 if (entryCache.containsEntry(entryDN)) { 129 return true; 130 } 131 } 132 133 return false; 134 } 135 136 137 /** 138 * {@inheritDoc} 139 */ 140 @Override 141 public Entry getEntry(DN entryDN, 142 LockType lockType, 143 List<Lock> lockList) 144 { 145 Entry entry = null; 146 147 for (EntryCache<? extends EntryCacheCfg> entryCache : cacheOrder) { 148 entry = entryCache.getEntry(entryDN, lockType, lockList); 149 if (entry != null) { 150 break; 151 } 152 } 153 154 // Indicate global cache miss. 155 if ((entry == null) && (cacheOrder.length != 0)) { 156 cacheMisses.getAndIncrement(); 157 } 158 159 return entry; 160 } 161 162 163 /** 164 * {@inheritDoc} 165 */ 166 @Override 167 public Entry getEntry(Backend backend, long entryID, 168 LockType lockType, 169 List<Lock> lockList) 170 { 171 Entry entry = null; 172 173 for (EntryCache<? extends EntryCacheCfg> entryCache : cacheOrder) { 174 entry = entryCache.getEntry(backend, entryID, lockType, 175 lockList); 176 if (entry != null) { 177 break; 178 } 179 } 180 181 // Indicate global cache miss. 182 if ((entry == null) && (cacheOrder.length != 0)) { 183 cacheMisses.getAndIncrement(); 184 } 185 186 return entry; 187 } 188 189 190 /** 191 * {@inheritDoc} 192 */ 193 public Entry getEntry(DN entryDN) 194 { 195 Entry entry = null; 196 197 for (EntryCache entryCache : cacheOrder) { 198 entry = entryCache.getEntry(entryDN); 199 if (entry != null) { 200 break; 201 } 202 } 203 204 // Indicate global cache miss. 205 if ((entry == null) && (cacheOrder.length != 0)) { 206 cacheMisses.getAndIncrement(); 207 } 208 209 return entry; 210 } 211 212 213 214 /** 215 * {@inheritDoc} 216 */ 217 public long getEntryID(DN entryDN) 218 { 219 long entryID = -1; 220 221 for (EntryCache entryCache : cacheOrder) { 222 entryID = entryCache.getEntryID(entryDN); 223 if (entryID != -1) { 224 break; 225 } 226 } 227 228 return entryID; 229 } 230 231 232 233 /** 234 * {@inheritDoc} 235 */ 236 public DN getEntryDN(Backend backend, long entryID) 237 { 238 DN entryDN = null; 239 240 for (EntryCache entryCache : cacheOrder) { 241 entryDN = entryCache.getEntryDN(backend, entryID); 242 if (entryDN != null) { 243 break; 244 } 245 } 246 247 return entryDN; 248 } 249 250 251 252 /** 253 * {@inheritDoc} 254 */ 255 public void putEntry(Entry entry, Backend backend, long entryID) 256 { 257 for (EntryCache entryCache : cacheOrder) { 258 // The first cache in the order which can take this entry 259 // gets it. 260 if (entryCache.filtersAllowCaching(entry)) { 261 entryCache.putEntry(entry, backend, entryID); 262 break; 263 } 264 } 265 } 266 267 268 269 /** 270 * {@inheritDoc} 271 */ 272 public boolean putEntryIfAbsent(Entry entry, Backend backend, long entryID) 273 { 274 for (EntryCache entryCache : cacheOrder) { 275 // The first cache in the order which can take this entry 276 // gets it. 277 if (entryCache.filtersAllowCaching(entry)) { 278 return entryCache.putEntryIfAbsent(entry, backend, entryID); 279 } 280 } 281 282 return false; 283 } 284 285 286 287 /** 288 * {@inheritDoc} 289 */ 290 public void removeEntry(DN entryDN) 291 { 292 for (EntryCache entryCache : cacheOrder) { 293 if (entryCache.containsEntry(entryDN)) { 294 entryCache.removeEntry(entryDN); 295 break; 296 } 297 } 298 } 299 300 301 302 /** 303 * {@inheritDoc} 304 */ 305 public void clear() 306 { 307 for (EntryCache entryCache : cacheOrder) { 308 entryCache.clear(); 309 } 310 } 311 312 313 314 /** 315 * {@inheritDoc} 316 */ 317 public void clearBackend(Backend backend) 318 { 319 for (EntryCache entryCache : cacheOrder) { 320 entryCache.clearBackend(backend); 321 } 322 } 323 324 325 326 /** 327 * {@inheritDoc} 328 */ 329 public void clearSubtree(DN baseDN) 330 { 331 for (EntryCache entryCache : cacheOrder) { 332 entryCache.clearSubtree(baseDN); 333 } 334 } 335 336 337 338 /** 339 * {@inheritDoc} 340 */ 341 public void handleLowMemory() 342 { 343 for (EntryCache entryCache : cacheOrder) { 344 entryCache.handleLowMemory(); 345 } 346 } 347 348 349 350 /** 351 * {@inheritDoc} 352 */ 353 public boolean isConfigurationChangeAcceptable( 354 EntryCacheCfg configuration, 355 List<Message> unacceptableReasons 356 ) 357 { 358 // No implementation required. 359 return true; 360 } 361 362 363 364 /** 365 * {@inheritDoc} 366 */ 367 public ConfigChangeResult applyConfigurationChange( 368 EntryCacheCfg configuration 369 ) 370 { 371 // No implementation required. 372 ConfigChangeResult changeResult = new ConfigChangeResult( 373 ResultCode.SUCCESS, false, new ArrayList<Message>() 374 ); 375 376 return changeResult; 377 } 378 379 380 381 /** 382 * {@inheritDoc} 383 */ 384 public ArrayList<Attribute> getMonitorData() 385 { 386 ArrayList<Attribute> attrs = new ArrayList<Attribute>(); 387 388 // The sum of cache hits of all active entry cache 389 // implementations. 390 Long entryCacheHits = new Long(0); 391 // Common for all active entry cache implementations. 392 Long entryCacheMisses = new Long(cacheMisses.longValue()); 393 // The sum of cache counts of all active entry cache 394 // implementations. 395 Long currentEntryCacheCount = new Long(0); 396 397 for (EntryCache entryCache : cacheOrder) { 398 // Get cache hits and counts from every active cache. 399 entryCacheHits += entryCache.getCacheHits(); 400 currentEntryCacheCount += entryCache.getCacheCount(); 401 } 402 403 try { 404 attrs = EntryCacheCommon.getGenericMonitorData( 405 entryCacheHits, 406 entryCacheMisses, 407 null, 408 null, 409 currentEntryCacheCount, 410 null 411 ); 412 } catch (Exception e) { 413 if (debugEnabled()) { 414 TRACER.debugCaught(DebugLogLevel.ERROR, e); 415 } 416 } 417 418 return attrs; 419 } 420 421 422 423 /** 424 * {@inheritDoc} 425 */ 426 public Long getCacheCount() 427 { 428 Long cacheCount = new Long(0); 429 430 for (EntryCache entryCache : cacheOrder) { 431 cacheCount += entryCache.getCacheCount(); 432 } 433 434 return cacheCount; 435 } 436 437 438 439 /** 440 * Return a verbose string representation of the current cache maps. 441 * This is useful primary for debugging and diagnostic purposes such 442 * as in the entry cache unit tests. 443 * @return String verbose string representation of the current cache 444 * maps in the following format: dn:id:backend 445 * one cache entry map representation per line 446 * or <CODE>null</CODE> if all maps are empty. 447 */ 448 private String toVerboseString() 449 { 450 String verboseString = new String(); 451 StringBuilder sb = new StringBuilder(); 452 453 for (EntryCache entryCache : cacheOrder) { 454 final Method[] cacheMethods = 455 entryCache.getClass().getDeclaredMethods(); 456 for (int i = 0; i < cacheMethods.length; ++i) { 457 if (cacheMethods[i].getName().equals("toVerboseString")) { 458 cacheMethods[i].setAccessible(true); 459 try { 460 Object cacheVerboseString = 461 cacheMethods[i].invoke(entryCache, (Object[]) null); 462 if (cacheVerboseString != null) { 463 sb.append((String) cacheVerboseString); 464 } 465 } catch (Exception e) { 466 if (debugEnabled()) { 467 TRACER.debugCaught(DebugLogLevel.ERROR, e); 468 } 469 } 470 } 471 } 472 } 473 474 verboseString = sb.toString(); 475 476 return (verboseString.length() > 0 ? verboseString : null); 477 } 478 479 480 481 /** 482 * Retrieves the current cache order array. 483 * 484 * @return The current cache order array. 485 */ 486 public final EntryCache<? extends EntryCacheCfg>[] getCacheOrder() 487 { 488 return this.cacheOrder; 489 } 490 491 492 493 /** 494 * Sets the current cache order array. 495 * 496 * @param cacheOrderMap The current cache order array. 497 */ 498 public final void setCacheOrder( 499 SortedMap<Integer, 500 EntryCache<? extends EntryCacheCfg>> cacheOrderMap) 501 { 502 this.cacheOrder = 503 cacheOrderMap.values().toArray(new EntryCache<?>[0]); 504 } 505 506 507 508 /** 509 * Performs any processing that may be required whenever a backend 510 * is initialized for use in the Directory Server. This method will 511 * be invoked after the backend has been initialized but before it 512 * has been put into service. 513 * 514 * @param backend The backend that has been initialized and is 515 * about to be put into service. 516 */ 517 public void performBackendInitializationProcessing(Backend backend) 518 { 519 // Do nothing. 520 } 521 522 523 524 /** 525 * Performs any processing that may be required whenever a backend 526 * is finalized. This method will be invoked after the backend has 527 * been taken out of service but before it has been finalized. 528 * 529 * @param backend The backend that has been taken out of service 530 * and is about to be finalized. 531 */ 532 public void performBackendFinalizationProcessing(Backend backend) 533 { 534 // Do not clear any backends if the server is shutting down. 535 if ( !(DirectoryServer.getInstance().isShuttingDown()) ) { 536 clearBackend(backend); 537 } 538 } 539 } 540