/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
* or http://forgerock.org/license/CDDLv1.0.html.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at legal-notices/CDDLv1_0.txt.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information:
* Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*
*
* Copyright 2008-2010 Sun Microsystems, Inc.
* Portions Copyright 2011-2013 ForgeRock AS
*/
package org.opends.server.extensions;
import org.opends.messages.Message;
import java.util.ArrayList;
import java.util.List;
import java.util.SortedMap;
import org.opends.server.admin.server.ConfigurationChangeListener;
import org.opends.server.admin.std.server.EntryCacheCfg;
import org.opends.server.api.Backend;
import org.opends.server.api.BackendInitializationListener;
import org.opends.server.api.EntryCache;
import org.opends.server.config.ConfigException;
import org.opends.server.core.DirectoryServer;
import org.opends.server.loggers.debug.DebugTracer;
import org.opends.server.types.Attribute;
import org.opends.server.types.ConfigChangeResult;
import org.opends.server.types.DN;
import org.opends.server.types.DebugLogLevel;
import org.opends.server.types.Entry;
import org.opends.server.types.InitializationException;
import org.opends.server.types.ResultCode;
import static org.opends.server.loggers.debug.DebugLogger.*;
/**
* This class defines the default entry cache which acts as an arbiter for
* every entry cache implementation configured and installed within the
* Directory Server or acts an an empty cache if no implementation specific
* entry cache is configured. It does not actually store any entries, so
* all calls to the entry cache public API are routed to underlying entry
* cache according to the current configuration order and preferences.
*/
public class DefaultEntryCache
extends EntryCache<EntryCacheCfg>
implements ConfigurationChangeListener<EntryCacheCfg>,
BackendInitializationListener
{
/**
* The tracer object for the debug logger.
*/
private static final DebugTracer TRACER = getTracer();
// The entry cache order array reflects all currently configured and
// active entry cache implementations in cache level specific order.
private static EntryCache<? extends EntryCacheCfg>[] cacheOrder =
new EntryCache<?>[0];
/**
* Creates a new instance of this default entry cache.
*/
public DefaultEntryCache()
{
super();
// Register with backend initialization listener to clear cache
// entries belonging to given backend that about to go offline.
DirectoryServer.registerBackendInitializationListener(this);
}
/**
* {@inheritDoc}
*/
@Override
public void initializeEntryCache(EntryCacheCfg configEntry)
throws ConfigException, InitializationException
{
// No implementation required.
}
/**
* {@inheritDoc}
*/
@Override
public void finalizeEntryCache()
{
for (EntryCache<?> entryCache : cacheOrder) {
entryCache.finalizeEntryCache();
}
// ReInitialize cache order array.
cacheOrder = new EntryCache<?>[0];
}
/**
* {@inheritDoc}
*/
@Override
public boolean containsEntry(DN entryDN)
{
if (entryDN == null) {
return false;
}
for (EntryCache<?> entryCache : cacheOrder) {
if (entryCache.containsEntry(entryDN)) {
return true;
}
}
return false;
}
/**
* {@inheritDoc}
*/
@Override
public Entry getEntry(Backend backend, long entryID)
{
for (EntryCache<? extends EntryCacheCfg> entryCache : cacheOrder)
{
Entry entry = entryCache.getEntry(backend, entryID);
if (entry != null)
{
return entry.duplicate(true);
}
}
// Indicate global cache miss.
if (cacheOrder.length != 0)
{
cacheMisses.getAndIncrement();
}
return null;
}
/**
* {@inheritDoc}
*/
@Override
public Entry getEntry(DN entryDN)
{
for (EntryCache<? extends EntryCacheCfg> entryCache : cacheOrder)
{
Entry entry = entryCache.getEntry(entryDN);
if (entry != null)
{
return entry.duplicate(true);
}
}
// Indicate global cache miss.
if (cacheOrder.length != 0)
{
cacheMisses.getAndIncrement();
}
return null;
}
/**
* {@inheritDoc}
*/
@Override
public long getEntryID(DN entryDN)
{
for (EntryCache<?> entryCache : cacheOrder)
{
long entryID = entryCache.getEntryID(entryDN);
if (entryID != -1)
{
return entryID;
}
}
return -1;
}
/**
* {@inheritDoc}
*/
@Override
public DN getEntryDN(Backend backend, long entryID)
{
for (EntryCache<?> entryCache : cacheOrder)
{
DN entryDN = entryCache.getEntryDN(backend, entryID);
if (entryDN != null)
{
return entryDN;
}
}
return null;
}
/**
* {@inheritDoc}
*/
@Override
public void putEntry(Entry entry, Backend backend, long entryID)
{
for (EntryCache<?> entryCache : cacheOrder) {
// The first cache in the order which can take this entry
// gets it.
if (entryCache.filtersAllowCaching(entry)) {
entryCache.putEntry(entry.duplicate(false),
backend, entryID);
break;
}
}
}
/**
* {@inheritDoc}
*/
@Override
public boolean putEntryIfAbsent(Entry entry, Backend backend, long entryID)
{
for (EntryCache<?> entryCache : cacheOrder) {
// The first cache in the order which can take this entry
// gets it.
if (entryCache.filtersAllowCaching(entry)) {
return entryCache.putEntryIfAbsent(entry.duplicate(false),
backend, entryID);
}
}
return false;
}
/**
* {@inheritDoc}
*/
@Override
public void removeEntry(DN entryDN)
{
for (EntryCache<?> entryCache : cacheOrder) {
if (entryCache.containsEntry(entryDN)) {
entryCache.removeEntry(entryDN);
break;
}
}
}
/**
* {@inheritDoc}
*/
@Override
public void clear()
{
for (EntryCache<?> entryCache : cacheOrder) {
entryCache.clear();
}
}
/**
* {@inheritDoc}
*/
@Override
public void clearBackend(Backend backend)
{
for (EntryCache<?> entryCache : cacheOrder) {
entryCache.clearBackend(backend);
}
}
/**
* {@inheritDoc}
*/
@Override
public void clearSubtree(DN baseDN)
{
for (EntryCache<?> entryCache : cacheOrder) {
entryCache.clearSubtree(baseDN);
}
}
/**
* {@inheritDoc}
*/
@Override
public void handleLowMemory()
{
for (EntryCache<?> entryCache : cacheOrder) {
entryCache.handleLowMemory();
}
}
/**
* {@inheritDoc}
*/
@Override
public boolean isConfigurationChangeAcceptable(
EntryCacheCfg configuration,
List<Message> unacceptableReasons
)
{
// No implementation required.
return true;
}
/**
* {@inheritDoc}
*/
@Override
public ConfigChangeResult applyConfigurationChange(
EntryCacheCfg configuration
)
{
// No implementation required.
ConfigChangeResult changeResult = new ConfigChangeResult(
ResultCode.SUCCESS, false, new ArrayList<Message>()
);
return changeResult;
}
/**
* {@inheritDoc}
*/
@Override
public ArrayList<Attribute> getMonitorData()
{
ArrayList<Attribute> attrs = new ArrayList<Attribute>();
// The sum of cache hits of all active entry cache
// implementations.
Long entryCacheHits = new Long(0);
// Common for all active entry cache implementations.
Long entryCacheMisses = new Long(cacheMisses.longValue());
// The sum of cache counts of all active entry cache
// implementations.
Long currentEntryCacheCount = new Long(0);
for (EntryCache<?> entryCache : cacheOrder) {
// Get cache hits and counts from every active cache.
entryCacheHits += entryCache.getCacheHits();
currentEntryCacheCount += entryCache.getCacheCount();
}
try {
attrs = EntryCacheCommon.getGenericMonitorData(
entryCacheHits,
entryCacheMisses,
null,
null,
currentEntryCacheCount,
null
);
} catch (Exception e) {
if (debugEnabled()) {
TRACER.debugCaught(DebugLogLevel.ERROR, e);
}
}
return attrs;
}
/**
* {@inheritDoc}
*/
@Override
public Long getCacheCount()
{
Long cacheCount = new Long(0);
for (EntryCache<?> entryCache : cacheOrder) {
cacheCount += entryCache.getCacheCount();
}
return cacheCount;
}
/**
* {@inheritDoc}
*/
public String toVerboseString()
{
StringBuilder sb = new StringBuilder();
for (EntryCache<?> entryCache : cacheOrder)
{
String s = entryCache.toVerboseString();
if (s != null)
{
sb.append(s);
}
}
String verboseString = sb.toString();
return (verboseString.length() > 0 ? verboseString : null);
}
/**
* Retrieves the current cache order array.
*
* @return The current cache order array.
*/
public final EntryCache<? extends EntryCacheCfg>[] getCacheOrder()
{
return DefaultEntryCache.cacheOrder;
}
/**
* Sets the current cache order array.
*
* @param cacheOrderMap The current cache order array.
*/
public final void setCacheOrder(
SortedMap<Integer,
EntryCache<? extends EntryCacheCfg>> cacheOrderMap)
{
DefaultEntryCache.cacheOrder =
cacheOrderMap.values().toArray(new EntryCache<?>[0]);
}
/**
* Performs any processing that may be required whenever a backend
* is initialized for use in the Directory Server. This method will
* be invoked after the backend has been initialized but before it
* has been put into service.
*
* @param backend The backend that has been initialized and is
* about to be put into service.
*/
@Override
public void performBackendInitializationProcessing(Backend backend)
{
// Do nothing.
}
/**
* Performs any processing that may be required whenever a backend
* is finalized. This method will be invoked after the backend has
* been taken out of service but before it has been finalized.
*
* @param backend The backend that has been taken out of service
* and is about to be finalized.
*/
@Override
public void performBackendFinalizationProcessing(Backend backend)
{
// Do not clear any backends if the server is shutting down.
if ( !(DirectoryServer.getInstance().isShuttingDown()) ) {
clearBackend(backend);
}
}
}