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