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 2008-2010 Sun Microsystems, Inc. 025 * Portions Copyright 2011-2015 ForgeRock AS 026 */ 027package org.opends.server.extensions; 028 029import java.util.Collections; 030import java.util.List; 031import java.util.SortedMap; 032 033import org.forgerock.i18n.LocalizableMessage; 034import org.forgerock.i18n.slf4j.LocalizedLogger; 035import org.forgerock.opendj.config.server.ConfigException; 036import org.opends.server.admin.server.ConfigurationChangeListener; 037import org.opends.server.admin.std.server.EntryCacheCfg; 038import org.opends.server.api.Backend; 039import org.opends.server.api.BackendInitializationListener; 040import org.opends.server.api.EntryCache; 041import org.opends.server.core.DirectoryServer; 042import org.opends.server.types.Attribute; 043import org.forgerock.opendj.config.server.ConfigChangeResult; 044import org.opends.server.types.DN; 045import org.opends.server.types.Entry; 046import org.opends.server.types.InitializationException; 047 048/** 049 * This class defines the default entry cache which acts as an arbiter for 050 * every entry cache implementation configured and installed within the 051 * Directory Server or acts an an empty cache if no implementation specific 052 * entry cache is configured. It does not actually store any entries, so 053 * all calls to the entry cache public API are routed to underlying entry 054 * cache according to the current configuration order and preferences. 055 */ 056public class DefaultEntryCache 057 extends EntryCache<EntryCacheCfg> 058 implements ConfigurationChangeListener<EntryCacheCfg>, 059 BackendInitializationListener 060{ 061 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 062 063 064 /** 065 * The entry cache order array reflects all currently configured and 066 * active entry cache implementations in cache level specific order. 067 */ 068 private static EntryCache<? extends EntryCacheCfg>[] cacheOrder = 069 new EntryCache<?>[0]; 070 071 072 /** 073 * Creates a new instance of this default entry cache. 074 */ 075 public DefaultEntryCache() 076 { 077 super(); 078 079 // Register with backend initialization listener to clear cache 080 // entries belonging to given backend that about to go offline. 081 DirectoryServer.registerBackendInitializationListener(this); 082 } 083 084 /** {@inheritDoc} */ 085 @Override 086 public void initializeEntryCache(EntryCacheCfg configEntry) 087 throws ConfigException, InitializationException 088 { 089 // No implementation required. 090 } 091 092 /** {@inheritDoc} */ 093 @Override 094 public void finalizeEntryCache() 095 { 096 for (EntryCache<?> entryCache : cacheOrder) { 097 entryCache.finalizeEntryCache(); 098 } 099 // ReInitialize cache order array. 100 cacheOrder = new EntryCache<?>[0]; 101 } 102 103 /** {@inheritDoc} */ 104 @Override 105 public boolean containsEntry(DN entryDN) 106 { 107 if (entryDN == null) { 108 return false; 109 } 110 111 for (EntryCache<?> entryCache : cacheOrder) { 112 if (entryCache.containsEntry(entryDN)) { 113 return true; 114 } 115 } 116 117 return false; 118 } 119 120 /** {@inheritDoc} */ 121 @Override 122 public Entry getEntry(String backendID, long entryID) 123 { 124 for (EntryCache<? extends EntryCacheCfg> entryCache : cacheOrder) 125 { 126 Entry entry = entryCache.getEntry(backendID, entryID); 127 if (entry != null) 128 { 129 return entry.duplicate(true); 130 } 131 } 132 133 // Indicate global cache miss. 134 if (cacheOrder.length != 0) 135 { 136 cacheMisses.getAndIncrement(); 137 } 138 return null; 139 } 140 141 /** {@inheritDoc} */ 142 @Override 143 public Entry getEntry(DN entryDN) 144 { 145 for (EntryCache<? extends EntryCacheCfg> entryCache : cacheOrder) 146 { 147 Entry entry = entryCache.getEntry(entryDN); 148 if (entry != null) 149 { 150 return entry.duplicate(true); 151 } 152 } 153 154 // Indicate global cache miss. 155 if (cacheOrder.length != 0) 156 { 157 cacheMisses.getAndIncrement(); 158 } 159 return null; 160 } 161 162 /** {@inheritDoc} */ 163 @Override 164 public long getEntryID(DN entryDN) 165 { 166 for (EntryCache<?> entryCache : cacheOrder) 167 { 168 long entryID = entryCache.getEntryID(entryDN); 169 if (entryID != -1) 170 { 171 return entryID; 172 } 173 } 174 return -1; 175 } 176 177 /** {@inheritDoc} */ 178 @Override 179 public DN getEntryDN(String backendID, long entryID) 180 { 181 for (EntryCache<?> entryCache : cacheOrder) 182 { 183 DN entryDN = entryCache.getEntryDN(backendID, entryID); 184 if (entryDN != null) 185 { 186 return entryDN; 187 } 188 } 189 return null; 190 } 191 192 /** {@inheritDoc} */ 193 @Override 194 public void putEntry(Entry entry, String backendID, long entryID) 195 { 196 for (EntryCache<?> entryCache : cacheOrder) { 197 // The first cache in the order which can take this entry 198 // gets it. 199 if (entryCache.filtersAllowCaching(entry)) { 200 entryCache.putEntry(entry.duplicate(false), backendID, entryID); 201 break; 202 } 203 } 204 } 205 206 /** {@inheritDoc} */ 207 @Override 208 public boolean putEntryIfAbsent(Entry entry, String backendID, long entryID) 209 { 210 for (EntryCache<?> entryCache : cacheOrder) { 211 // The first cache in the order which can take this entry 212 // gets it. 213 if (entryCache.filtersAllowCaching(entry)) { 214 return entryCache.putEntryIfAbsent(entry.duplicate(false), 215 backendID, entryID); 216 } 217 } 218 219 return false; 220 } 221 222 /** {@inheritDoc} */ 223 @Override 224 public void removeEntry(DN entryDN) 225 { 226 for (EntryCache<?> entryCache : cacheOrder) { 227 if (entryCache.containsEntry(entryDN)) { 228 entryCache.removeEntry(entryDN); 229 break; 230 } 231 } 232 } 233 234 /** {@inheritDoc} */ 235 @Override 236 public void clear() 237 { 238 for (EntryCache<?> entryCache : cacheOrder) { 239 entryCache.clear(); 240 } 241 } 242 243 /** {@inheritDoc} */ 244 @Override 245 public void clearBackend(String backendID) 246 { 247 for (EntryCache<?> entryCache : cacheOrder) { 248 entryCache.clearBackend(backendID); 249 } 250 } 251 252 /** {@inheritDoc} */ 253 @Override 254 public void clearSubtree(DN baseDN) 255 { 256 for (EntryCache<?> entryCache : cacheOrder) { 257 entryCache.clearSubtree(baseDN); 258 } 259 } 260 261 /** {@inheritDoc} */ 262 @Override 263 public void handleLowMemory() 264 { 265 for (EntryCache<?> entryCache : cacheOrder) { 266 entryCache.handleLowMemory(); 267 } 268 } 269 270 /** {@inheritDoc} */ 271 @Override 272 public boolean isConfigurationChangeAcceptable( 273 EntryCacheCfg configuration, 274 List<LocalizableMessage> unacceptableReasons 275 ) 276 { 277 // No implementation required. 278 return true; 279 } 280 281 /** {@inheritDoc} */ 282 @Override 283 public ConfigChangeResult applyConfigurationChange(EntryCacheCfg configuration) 284 { 285 // No implementation required. 286 return new ConfigChangeResult(); 287 } 288 289 /** {@inheritDoc} */ 290 @Override 291 public List<Attribute> getMonitorData() 292 { 293 // The sum of cache hits of all active entry cache implementations. 294 long entryCacheHits = 0; 295 // Common for all active entry cache implementations. 296 long entryCacheMisses = cacheMisses.longValue(); 297 // The sum of cache counts of all active entry cache implementations. 298 long currentEntryCacheCount = 0; 299 300 for (EntryCache<?> entryCache : cacheOrder) { 301 // Get cache hits and counts from every active cache. 302 entryCacheHits += entryCache.getCacheHits(); 303 currentEntryCacheCount += entryCache.getCacheCount(); 304 } 305 306 try { 307 return EntryCacheCommon.getGenericMonitorData( 308 entryCacheHits, 309 entryCacheMisses, 310 null, 311 null, 312 currentEntryCacheCount, 313 null 314 ); 315 } catch (Exception e) { 316 logger.traceException(e); 317 return Collections.emptyList(); 318 } 319 } 320 321 /** {@inheritDoc} */ 322 @Override 323 public Long getCacheCount() 324 { 325 long cacheCount = 0; 326 for (EntryCache<?> entryCache : cacheOrder) { 327 cacheCount += entryCache.getCacheCount(); 328 } 329 return cacheCount; 330 } 331 332 /** {@inheritDoc} */ 333 @Override 334 public String toVerboseString() 335 { 336 StringBuilder sb = new StringBuilder(); 337 for (EntryCache<?> entryCache : cacheOrder) 338 { 339 String s = entryCache.toVerboseString(); 340 if (s != null) 341 { 342 sb.append(s); 343 } 344 } 345 String verboseString = sb.toString(); 346 return verboseString.length() > 0 ? verboseString : null; 347 } 348 349 350 351 /** 352 * Retrieves the current cache order array. 353 * 354 * @return The current cache order array. 355 */ 356 public final static EntryCache<? extends EntryCacheCfg>[] getCacheOrder() 357 { 358 return DefaultEntryCache.cacheOrder; 359 } 360 361 362 363 /** 364 * Sets the current cache order array. 365 * 366 * @param cacheOrderMap The current cache order array. 367 */ 368 public final static void setCacheOrder( 369 SortedMap<Integer, 370 EntryCache<? extends EntryCacheCfg>> cacheOrderMap) 371 { 372 DefaultEntryCache.cacheOrder = 373 cacheOrderMap.values().toArray(new EntryCache<?>[0]); 374 } 375 376 377 378 /** 379 * Performs any processing that may be required whenever a backend 380 * is initialized for use in the Directory Server. This method will 381 * be invoked after the backend has been initialized but before it 382 * has been put into service. 383 * 384 * @param backend The backend that has been initialized and is 385 * about to be put into service. 386 */ 387 @Override 388 public void performBackendPreInitializationProcessing(Backend<?> backend) 389 { 390 // Do nothing. 391 } 392 393 394 395 /** 396 * Performs any processing that may be required whenever a backend 397 * is finalized. This method will be invoked after the backend has 398 * been taken out of service but before it has been finalized. 399 * 400 * @param backend The backend that has been taken out of service 401 * and is about to be finalized. 402 */ 403 @Override 404 public void performBackendPostFinalizationProcessing(Backend<?> backend) 405 { 406 // Do not clear any backends if the server is shutting down. 407 if (!DirectoryServer.getInstance().isShuttingDown()) 408 { 409 clearBackend(backend.getBackendID()); 410 } 411 } 412 413 @Override 414 public void performBackendPostInitializationProcessing(Backend<?> backend) { 415 // Nothing to do. 416 } 417 418 @Override 419 public void performBackendPreFinalizationProcessing(Backend<?> backend) { 420 // Nothing to do. 421 } 422}