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