/* * 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 2014 ForgeRock AS */ package org.opends.server.backends.persistit; import org.forgerock.i18n.slf4j.LocalizedLogger; import org.opends.server.api.EntryCache; import org.opends.server.backends.jeb.EntryID; import org.opends.server.backends.pluggable.SuffixContainer; import org.opends.server.core.DirectoryServer; import org.opends.server.types.DN; import org.opends.server.types.DirectoryException; import org.opends.server.types.Entry; import com.persistit.Exchange; import com.persistit.Persistit; import com.persistit.Volume; import com.persistit.exception.PersistitException; import com.sleepycat.je.DatabaseException; import static org.opends.messages.JebMessages.*; import static org.opends.server.core.DirectoryServer.*; /** * Persistit implementation of a {@link SuffixContainer}. */ class PersistitSuffixContainer implements SuffixContainer { private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); /** The baseDN handled by this suffix container. */ private final DN baseDN; /** * Prevents name clashes for common indexes (like id2entry) across multiple * suffixes. For example when a root container contains multiple suffixes. */ private final String indexPrefix; private final PersistitRootContainer rootContainer; private final Persistit db; private final Volume volume; private PersistitID2Entry id2entry; private PersistitDN2ID dn2id; /** * Constructor for this class. * * @param baseDN * The baseDN handled by this suffix container * @param indexPrefix * the prefix to use for indexes name * @param rootContainer * the persisit root container * @param db * the persisit database * @param volume * the volume where indexes will be created */ PersistitSuffixContainer(DN baseDN, String indexPrefix, PersistitRootContainer rootContainer, Persistit db, Volume volume) { this.baseDN = baseDN; this.indexPrefix = indexPrefix; this.rootContainer = rootContainer; this.db = db; this.volume = volume; } /** * Opens the entryContainer for reading and writing. * * @throws DirectoryException * If an error occurs while opening the suffix container. */ void open() throws DirectoryException { id2entry = new PersistitID2Entry(this); id2entry.open(); dn2id = new PersistitDN2ID(this); dn2id.open(); } /** {@inheritDoc} */ @Override public void close() { dn2id.close(); id2entry.close(); } /** {@inheritDoc} */ @Override public DN getBaseDN() { return baseDN; } /** * Returns the index name prefix. * * @return the index name prefix */ public String getIndexPrefix() { return indexPrefix; } /** {@inheritDoc} */ @Override public long getEntryCount() { // FIXME To be implemented // JNR: I have not found a suitable API to return this value return -1; } /** * Returns the id2entry index. * * @return the id2entry index */ public PersistitID2Entry getID2Entry() { return id2entry; } /** * Creates a {@link Tree} for the fully qualified index name. * * @param fullyQualifiedIndexName * The fully qualified index name * @throws PersistitException * if a database exception happens */ void createTree(String fullyQualifiedIndexName) throws PersistitException { volume.getTree(fullyQualifiedIndexName, true); } /** * Returns a new {@link Exchange} working on the index provided via its fully * qualified name. *

* Note: exchanges obtained with this method must be released by calling * {@link #releaseExchange(Exchange)}. * * @param fullyQualifiedIndexName * The fully qualified index name * @return the exchange * @throws PersistitException * if a database exception happens */ final Exchange getExchange(String fullyQualifiedIndexName) throws PersistitException { return db.getExchange(volume, fullyQualifiedIndexName, false); } /** * Returns the fully qualified index name for this simple index name. *

* e.g. * *

   * PersistitSuffixContainer sc = ...; // initialize the suffix container
   * assertEquals(sc.getIndexPrefix(), "dccom");
   * assertEquals(sc.getFullyQualifiedIndexName("id2entry"), "dccom_id2entry");
   * 
* * @param simpleIndexName * the simple index name to convert to a fully qualified index name * @return the fully qualified index name */ public String getFullyQualifiedIndexName(String simpleIndexName) { return indexPrefix + "_" + simpleIndexName; } /** * Releases the provided exchange. * * @param exchange * the exchange to release */ final void releaseExchange(Exchange exchange) { if (exchange != null) { db.releaseExchange(exchange); } } /** {@inheritDoc} */ @Override public Entry getEntry(EntryID entryID) throws DirectoryException { // Try the entry cache first. final EntryCache entryCache = getEntryCache(); final PersistitBackend backend = rootContainer.getBackend(); final Entry cacheEntry = entryCache.getEntry(backend, entryID.longValue()); if (cacheEntry != null) { return cacheEntry; } final Entry entry = id2entry.get(null, entryID, null); if (entry != null) { // Put the entry in the cache making sure not to overwrite a newer copy // that may have been inserted since the time we read the cache. entryCache.putEntryIfAbsent(entry, backend, entryID.longValue()); } return entry; } /** * Indicates whether an entry with the specified DN exists. * * @param entryDN * The DN of the entry for which to determine existence. * @return true if the specified entry exists, or * false if it does not. * @throws DirectoryException * If a problem occurs while trying to make the determination. */ public boolean entryExists(DN entryDN) throws DirectoryException { // Try the entry cache first. EntryCache entryCache = DirectoryServer.getEntryCache(); if (entryCache != null && entryCache.containsEntry(entryDN)) { return true; } try { // Read the ID from dn2id. EntryID id = dn2id.get(null, entryDN, null); return id != null; } catch (DatabaseException e) { logger.traceException(e); return false; } } /** * Retrieves and returns the entry associated to the provided DN. * * @param entryDN * the DN of the entry to return * @return the entry associated to the provided DN * @throws DirectoryException * if a directory exception happens * @throws PersistitException * if a database exception happens */ Entry getEntry(DN entryDN) throws DirectoryException, PersistitException { EntryCache entryCache = DirectoryServer.getEntryCache(); // Try the entry cache first. Entry entry = null; if (entryCache != null) { entry = entryCache.getEntry(entryDN); } if (entry == null) { // Read dn2id. EntryID entryID = dn2id.get(null, entryDN, null); if (entryID == null) { // The entryDN does not exist. // Check for referral entries above the target entry. // TODO JNR uncomment next line // dn2uri.targetEntryReferrals(entryDN, null); return null; } // Read id2entry. entry = id2entry.get(null, entryID, null); if (entry == null) { // The entryID does not exist. throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), ERR_JEB_MISSING_ID2ENTRY_RECORD .get(entryID)); } // Put the entry in the cache making sure not to overwrite // a newer copy that may have been inserted since the time // we read the cache. if (entryCache != null) { entryCache.putEntryIfAbsent(entry, rootContainer.getBackend(), entryID.longValue()); } } return entry; } /** {@inheritDoc} */ @Override public String toString() { return getClass().getSimpleName() + " baseDN=" + baseDN; } }