| | |
| | | import org.forgerock.opendj.ldap.ByteString; |
| | | import org.forgerock.opendj.ldap.ResultCode; |
| | | import org.forgerock.opendj.ldap.SearchScope; |
| | | import org.forgerock.util.Utils; |
| | | import org.opends.server.admin.server.ConfigurationAddListener; |
| | | import org.opends.server.admin.server.ConfigurationChangeListener; |
| | | import org.opends.server.admin.server.ConfigurationDeleteListener; |
| | |
| | | import org.opends.server.api.ClientConnection; |
| | | import org.opends.server.api.EntryCache; |
| | | import org.opends.server.api.plugin.PluginResult; |
| | | import org.opends.server.backends.pluggable.SuffixContainer; |
| | | import org.opends.server.controls.*; |
| | | import org.opends.server.core.*; |
| | | import org.opends.server.protocols.ldap.LDAPResultCode; |
| | | import org.opends.server.types.*; |
| | | import org.opends.server.util.ServerConstants; |
| | | import org.opends.server.util.StaticUtils; |
| | | |
| | | import com.sleepycat.je.*; |
| | | |
| | | import static com.sleepycat.je.LockMode.*; |
| | | |
| | | import static org.opends.messages.JebMessages.*; |
| | | import static org.opends.server.protocols.ldap.LDAPResultCode.*; |
| | | import static org.opends.server.util.StaticUtils.*; |
| | | |
| | | /** |
| | |
| | | * the guts of the backend API methods for LDAP operations. |
| | | */ |
| | | public class EntryContainer |
| | | implements ConfigurationChangeListener<LocalDBBackendCfg> |
| | | implements SuffixContainer, ConfigurationChangeListener<LocalDBBackendCfg> |
| | | { |
| | | private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); |
| | | |
| | |
| | | /** The vlv index configuration manager. */ |
| | | private final VLVJEIndexCfgManager vlvJEIndexCfgManager; |
| | | |
| | | /** The backend to which this entry entryContainer belongs. */ |
| | | /** The backend to which this entry container belongs. */ |
| | | private final Backend<?> backend; |
| | | |
| | | /** The root container in which this entryContainer belongs. */ |
| | |
| | | /** The set of VLV (Virtual List View) indexes. */ |
| | | private final HashMap<String, VLVIndex> vlvIndexMap = new HashMap<String, VLVIndex>(); |
| | | |
| | | /** |
| | | * Prevents name clashes for common indexes (like id2entry) across multiple suffixes. |
| | | * For example when a root container contains multiple suffixes. |
| | | */ |
| | | private String databasePrefix; |
| | | |
| | | /** |
| | |
| | | final Lock exclusiveLock = lock.writeLock(); |
| | | |
| | | /** |
| | | * Create a new entry entryContainer object. |
| | | * Create a new entry container object. |
| | | * |
| | | * @param baseDN The baseDN this entry container will be responsible for |
| | | * storing on disk. |
| | |
| | | logger.info(NOTE_JEB_SUBORDINATE_INDEXES_DISABLED, backend.getBackendID()); |
| | | } |
| | | |
| | | dn2uri = new DN2URI(databasePrefix + "_" + REFERRAL_DATABASE_NAME, |
| | | env, this); |
| | | dn2uri = new DN2URI(databasePrefix + "_" + REFERRAL_DATABASE_NAME, env, this); |
| | | dn2uri.open(); |
| | | |
| | | for (String idx : config.listLocalDBIndexes()) |
| | |
| | | } |
| | | |
| | | /** |
| | | * Closes the entry entryContainer. |
| | | * Closes the entry container. |
| | | * |
| | | * @throws DatabaseException If an error occurs in the JE database. |
| | | */ |
| | | public void close() |
| | | throws DatabaseException |
| | | public void close() throws DatabaseException |
| | | { |
| | | // Close core indexes. |
| | | dn2id.close(); |
| | |
| | | id2subtree.close(); |
| | | state.close(); |
| | | |
| | | // Close attribute indexes and deregister any listeners. |
| | | for (AttributeIndex index : attrIndexMap.values()) |
| | | { |
| | | index.close(); |
| | | } |
| | | Utils.closeSilently(attrIndexMap.values()); |
| | | |
| | | // Close VLV indexes and deregister any listeners. |
| | | for (VLVIndex vlvIndex : vlvIndexMap.values()) |
| | | { |
| | | vlvIndex.close(); |
| | | } |
| | | |
| | | // Deregister any listeners. |
| | | config.removeLocalDBChangeListener(this); |
| | | config.removeLocalDBIndexAddListener(attributeJEIndexCfgManager); |
| | | config.removeLocalDBIndexDeleteListener(attributeJEIndexCfgManager); |
| | |
| | | } |
| | | |
| | | /** |
| | | * Get the DN database used by this entry entryContainer. The entryContainer |
| | | * must have been opened. |
| | | * Get the DN database used by this entry container. |
| | | * The entryContainer must have been opened. |
| | | * |
| | | * @return The DN database. |
| | | */ |
| | |
| | | } |
| | | |
| | | /** |
| | | * Get the entry database used by this entry entryContainer. The |
| | | * entryContainer must have been opened. |
| | | * Get the entry database used by this entry container. |
| | | * The entryContainer must have been opened. |
| | | * |
| | | * @return The entry database. |
| | | */ |
| | |
| | | } |
| | | |
| | | /** |
| | | * Get the referral database used by this entry entryContainer. The |
| | | * entryContainer must have been opened. |
| | | * Get the referral database used by this entry container. |
| | | * The entryContainer must have been opened. |
| | | * |
| | | * @return The referral database. |
| | | */ |
| | |
| | | } |
| | | |
| | | /** |
| | | * Get the children database used by this entry entryContainer. |
| | | * Get the children database used by this entry container. |
| | | * The entryContainer must have been opened. |
| | | * |
| | | * @return The children database. |
| | |
| | | } |
| | | |
| | | /** |
| | | * Get the subtree database used by this entry entryContainer. |
| | | * Get the subtree database used by this entry container. |
| | | * The entryContainer must have been opened. |
| | | * |
| | | * @return The subtree database. |
| | |
| | | return attrIndexMap.get(attrType); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Return attribute index map. |
| | | * |
| | |
| | | */ |
| | | public EntryID getHighestEntryID() throws DatabaseException |
| | | { |
| | | EntryID entryID = new EntryID(0); |
| | | Cursor cursor = id2entry.openCursor(null, null); |
| | | DatabaseEntry key = new DatabaseEntry(); |
| | | DatabaseEntry data = new DatabaseEntry(); |
| | | |
| | | // Position a cursor on the last data item, and the key should |
| | | // give the highest ID. |
| | | try |
| | | { |
| | | OperationStatus status = cursor.getLast(key, data, LockMode.DEFAULT); |
| | | if (status == OperationStatus.SUCCESS) |
| | | // Position a cursor on the last data item, and the key should give the highest ID. |
| | | DatabaseEntry key = new DatabaseEntry(); |
| | | DatabaseEntry data = new DatabaseEntry(); |
| | | |
| | | if (cursor.getLast(key, data, DEFAULT) == OperationStatus.SUCCESS) |
| | | { |
| | | entryID = new EntryID(key); |
| | | return new EntryID(key); |
| | | } |
| | | return new EntryID(0); |
| | | } |
| | | finally |
| | | { |
| | | cursor.close(); |
| | | } |
| | | return entryID; |
| | | } |
| | | |
| | | /** |
| | |
| | | the searchResultDone message, and not send back any search result |
| | | entries. |
| | | */ |
| | | searchOperation.addResponseControl( |
| | | new ServerSideSortResponseControl( |
| | | LDAPResultCode.NO_SUCH_ATTRIBUTE, null)); |
| | | searchOperation.addResponseControl(new ServerSideSortResponseControl(NO_SUCH_ATTRIBUTE, null)); |
| | | searchOperation.setResultCode(ResultCode.UNAVAILABLE_CRITICAL_EXTENSION); |
| | | return; |
| | | } |
| | |
| | | { |
| | | try |
| | | { |
| | | entryIDList = |
| | | vlvIndex.evaluate(null, searchOperation, sortRequest, vlvRequest, |
| | | debugBuffer); |
| | | entryIDList = vlvIndex.evaluate(null, searchOperation, sortRequest, vlvRequest, debugBuffer); |
| | | if(entryIDList != null) |
| | | { |
| | | searchOperation.addResponseControl( |
| | | new ServerSideSortResponseControl(LDAPResultCode.SUCCESS, |
| | | null)); |
| | | searchOperation.addResponseControl(new ServerSideSortResponseControl(SUCCESS, null)); |
| | | candidatesAreInScope = true; |
| | | break; |
| | | } |
| | | } |
| | | catch (DirectoryException de) |
| | | { |
| | | searchOperation.addResponseControl( |
| | | new ServerSideSortResponseControl( |
| | | de.getResultCode().intValue(), null)); |
| | | searchOperation.addResponseControl(new ServerSideSortResponseControl(de.getResultCode().intValue(), null)); |
| | | |
| | | if (sortRequest.isCritical()) |
| | | { |
| | |
| | | vlvRequest); |
| | | if(sortRequest.containsSortKeys()) |
| | | { |
| | | searchOperation.addResponseControl( |
| | | new ServerSideSortResponseControl( |
| | | LDAPResultCode.SUCCESS, null)); |
| | | searchOperation.addResponseControl(new ServerSideSortResponseControl(SUCCESS, null)); |
| | | } |
| | | else |
| | | { |
| | |
| | | server return all search results unsorted and include the |
| | | sortKeyResponseControl in the searchResultDone message. |
| | | */ |
| | | searchOperation.addResponseControl( |
| | | new ServerSideSortResponseControl |
| | | (LDAPResultCode.NO_SUCH_ATTRIBUTE, null)); |
| | | searchOperation.addResponseControl(new ServerSideSortResponseControl(NO_SUCH_ATTRIBUTE, null)); |
| | | } |
| | | } |
| | | catch (DirectoryException de) |
| | | { |
| | | searchOperation.addResponseControl( |
| | | new ServerSideSortResponseControl( |
| | | de.getResultCode().intValue(), null)); |
| | | searchOperation.addResponseControl(new ServerSideSortResponseControl(de.getResultCode().intValue(), null)); |
| | | |
| | | if (sortRequest.isCritical()) |
| | | { |
| | |
| | | debugBuffer.append(" final="); |
| | | entryIDList.toString(debugBuffer); |
| | | |
| | | Attribute attr = Attributes.create(ATTR_DEBUG_SEARCH_INDEX, |
| | | debugBuffer.toString()); |
| | | |
| | | Entry debugEntry = |
| | | new Entry(DN.valueOf("cn=debugsearch"), null, null, null); |
| | | Attribute attr = Attributes.create(ATTR_DEBUG_SEARCH_INDEX, debugBuffer.toString()); |
| | | Entry debugEntry = new Entry(DN.valueOf("cn=debugsearch"), null, null, null); |
| | | debugEntry.addAttribute(attr, new ArrayList<ByteString>()); |
| | | |
| | | searchOperation.returnEntry(debugEntry, null); |
| | |
| | | { |
| | | rootContainer.getMonitorProvider().updateIndexedSearchCount(); |
| | | } |
| | | searchIndexed(entryIDList, candidatesAreInScope, searchOperation, |
| | | pageRequest); |
| | | searchIndexed(entryIDList, candidatesAreInScope, searchOperation, pageRequest); |
| | | } |
| | | else |
| | | { |
| | |
| | | rootContainer.getMonitorProvider().updateUnindexedSearchCount(); |
| | | } |
| | | |
| | | searchOperation.addAdditionalLogItem( |
| | | AdditionalLogItem.keyOnly(getClass(), "unindexed")); |
| | | searchOperation.addAdditionalLogItem(AdditionalLogItem.keyOnly(getClass(), "unindexed")); |
| | | |
| | | // See if we could use a virtual attribute rule to process the search. |
| | | for (VirtualAttributeRule rule : DirectoryServer.getVirtualAttributes()) |
| | |
| | | } |
| | | } |
| | | |
| | | ClientConnection clientConnection = |
| | | searchOperation.getClientConnection(); |
| | | if(! clientConnection.hasPrivilege(Privilege.UNINDEXED_SEARCH, |
| | | searchOperation)) |
| | | ClientConnection clientConnection = searchOperation.getClientConnection(); |
| | | if (!clientConnection.hasPrivilege(Privilege.UNINDEXED_SEARCH, searchOperation)) |
| | | { |
| | | LocalizableMessage message = |
| | | ERR_JEB_SEARCH_UNINDEXED_INSUFFICIENT_PRIVILEGES.get(); |
| | | throw new DirectoryException(ResultCode.INSUFFICIENT_ACCESS_RIGHTS, |
| | | message); |
| | | LocalizableMessage message = ERR_JEB_SEARCH_UNINDEXED_INSUFFICIENT_PRIVILEGES.get(); |
| | | throw new DirectoryException(ResultCode.INSUFFICIENT_ACCESS_RIGHTS, message); |
| | | } |
| | | |
| | | if (sortRequest != null) |
| | | { |
| | | // FIXME -- Add support for sorting unindexed searches using indexes |
| | | // like DSEE currently does. |
| | | searchOperation.addResponseControl( |
| | | new ServerSideSortResponseControl( |
| | | LDAPResultCode.UNWILLING_TO_PERFORM, null)); |
| | | searchOperation.addResponseControl(new ServerSideSortResponseControl(UNWILLING_TO_PERFORM, null)); |
| | | |
| | | if (sortRequest.isCritical()) |
| | | { |
| | |
| | | * @throws DirectoryException If an error prevented the search from being |
| | | * processed. |
| | | */ |
| | | private void searchNotIndexed(SearchOperation searchOperation, |
| | | PagedResultsControl pageRequest) |
| | | throws DirectoryException, CanceledOperationException |
| | | private void searchNotIndexed(SearchOperation searchOperation, PagedResultsControl pageRequest) |
| | | throws DirectoryException, CanceledOperationException |
| | | { |
| | | EntryCache<?> entryCache = DirectoryServer.getEntryCache(); |
| | | DN aBaseDN = searchOperation.getBaseDN(); |
| | |
| | | logger.traceException(e); |
| | | String str = pageRequest.getCookie().toHexString(); |
| | | LocalizableMessage msg = ERR_JEB_INVALID_PAGED_RESULTS_COOKIE.get(str); |
| | | throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, |
| | | msg, e); |
| | | throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, msg, e); |
| | | } |
| | | } |
| | | else |
| | |
| | | DatabaseEntry key = new DatabaseEntry(begin); |
| | | |
| | | int lookthroughCount = 0; |
| | | int lookthroughLimit = |
| | | searchOperation.getClientConnection().getLookthroughLimit(); |
| | | int lookthroughLimit = searchOperation.getClientConnection().getLookthroughLimit(); |
| | | |
| | | try |
| | | { |
| | | Cursor cursor = dn2id.openCursor(null, null); |
| | | try |
| | | { |
| | | OperationStatus status; |
| | | |
| | | // Initialize the cursor very close to the starting value. |
| | | status = cursor.getSearchKeyRange(key, data, LockMode.DEFAULT); |
| | | OperationStatus status = cursor.getSearchKeyRange(key, data, LockMode.DEFAULT); |
| | | |
| | | // Step forward until we pass the ending value. |
| | | while (status == OperationStatus.SUCCESS) |
| | |
| | | if ((manageDsaIT || entry.getReferralURLs() == null) |
| | | && searchOperation.getFilter().matchesEntry(entry)) |
| | | { |
| | | if (pageRequest != null && |
| | | searchOperation.getEntriesSent() == |
| | | pageRequest.getSize()) |
| | | if (pageRequest != null |
| | | && searchOperation.getEntriesSent() == pageRequest.getSize()) |
| | | { |
| | | // The current page is full. |
| | | // Set the cookie to remember where we were. |
| | |
| | | Cursor cursor = dn2id.openCursor(txn, cursorConfig); |
| | | try |
| | | { |
| | | OperationStatus status; |
| | | |
| | | // Initialize the cursor very close to the starting value. |
| | | status = cursor.getSearchKeyRange(key, data, LockMode.DEFAULT); |
| | | OperationStatus status = cursor.getSearchKeyRange(key, data, LockMode.DEFAULT); |
| | | |
| | | // Step forward until the key is greater than the starting value. |
| | | while (status == OperationStatus.SUCCESS && |
| | |
| | | // Read the entry ID from dn2id. |
| | | if(leafDNKey == null) |
| | | { |
| | | leafDNKey = |
| | | new DatabaseEntry(JebFormat.dnToDNKey( |
| | | targetDN, this.baseDN.size())); |
| | | leafDNKey = new DatabaseEntry(JebFormat.dnToDNKey(targetDN, this.baseDN.size())); |
| | | } |
| | | DatabaseEntry value = new DatabaseEntry(); |
| | | OperationStatus status; |
| | | status = dn2id.read(txn, leafDNKey, value, LockMode.RMW); |
| | | OperationStatus status = dn2id.read(txn, leafDNKey, value, LockMode.RMW); |
| | | if (status != OperationStatus.SUCCESS) |
| | | { |
| | | LocalizableMessage message = |
| | |
| | | * @throws DirectoryException If a problem occurs while trying to make the |
| | | * determination. |
| | | */ |
| | | public boolean entryExists(DN entryDN) |
| | | throws DirectoryException |
| | | public boolean entryExists(DN entryDN) throws DirectoryException |
| | | { |
| | | // Try the entry cache first. |
| | | EntryCache<?> entryCache = DirectoryServer.getEntryCache(); |
| | | if (entryCache != null && entryCache.containsEntry(entryDN)) |
| | | { |
| | | return true; |
| | | return true; |
| | | } |
| | | |
| | | try |
| | |
| | | catch (DatabaseException e) |
| | | { |
| | | logger.traceException(e); |
| | | return false; |
| | | } |
| | | |
| | | return false; |
| | | } |
| | | |
| | | /** |
| | |
| | | Cursor cursor = dn2id.openCursor(txn, cursorConfig); |
| | | try |
| | | { |
| | | OperationStatus status; |
| | | |
| | | // Initialize the cursor very close to the starting value. |
| | | status = cursor.getSearchKeyRange(key, data, LockMode.DEFAULT); |
| | | OperationStatus status = cursor.getSearchKeyRange(key, data, LockMode.DEFAULT); |
| | | |
| | | // Step forward until the key is greater than the starting value. |
| | | while (status == OperationStatus.SUCCESS && |
| | |
| | | } |
| | | |
| | | /** |
| | | * Get a count of the number of entries stored in this entry entryContainer. |
| | | * Get a count of the number of entries stored in this entry container. |
| | | * |
| | | * @return The number of entries stored in this entry entryContainer. |
| | | * @return The number of entries stored in this entry container. |
| | | * @throws DatabaseException If an error occurs in the JE database. |
| | | */ |
| | | @Override |
| | | public long getEntryCount() throws DatabaseException |
| | | { |
| | | EntryID entryID = dn2id.get(null, baseDN, LockMode.DEFAULT); |
| | | if (entryID != null) |
| | | { |
| | | DatabaseEntry key = |
| | | new DatabaseEntry(JebFormat.entryIDToDatabase(entryID.longValue())); |
| | | EntryIDSet entryIDSet; |
| | | entryIDSet = id2subtree.readKey(key, null, LockMode.DEFAULT); |
| | | DatabaseEntry key = new DatabaseEntry(JebFormat.entryIDToDatabase(entryID.longValue())); |
| | | EntryIDSet entryIDSet = id2subtree.readKey(key, null, LockMode.DEFAULT); |
| | | |
| | | long count = entryIDSet.size(); |
| | | if(count != Long.MAX_VALUE) |
| | |
| | | |
| | | /** |
| | | * Get the number of values for which the entry limit has been exceeded |
| | | * since the entry entryContainer was opened. |
| | | * since the entry container was opened. |
| | | * @return The number of values for which the entry limit has been exceeded. |
| | | */ |
| | | public int getEntryLimitExceededCount() |
| | |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Get the baseDN this entry container is responsible for. |
| | | * |
| | | * @return The Base DN for this entry container. |
| | | */ |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public DN getBaseDN() |
| | | { |
| | | return baseDN; |
| | |
| | | * @throws DatabaseException If an error occurs while retrieving the |
| | | * configuration object. |
| | | */ |
| | | public EnvironmentConfig getEnvironmentConfig() |
| | | throws DatabaseException |
| | | public EnvironmentConfig getEnvironmentConfig() throws DatabaseException |
| | | { |
| | | return env.getConfig(); |
| | | } |
| | |
| | | * @throws DirectoryException If an error prevented the check of an |
| | | * existing entry from being performed. |
| | | */ |
| | | private DN getMatchedDN(DN baseDN) |
| | | throws DirectoryException |
| | | private DN getMatchedDN(DN baseDN) throws DirectoryException |
| | | { |
| | | DN parentDN = baseDN.getParentDNInSuffix(); |
| | | while (parentDN != null && parentDN.isDescendantOf(getBaseDN())) |
| | |
| | | */ |
| | | private void openSubordinateIndexes() |
| | | { |
| | | id2children = new Index(databasePrefix + "_" |
| | | + ID2CHILDREN_DATABASE_NAME, new ID2CIndexer(), state, |
| | | config.getIndexEntryLimit(), 0, true, env, this); |
| | | id2children.open(); |
| | | if (!id2children.isTrusted()) |
| | | id2children = newIndex(ID2CHILDREN_DATABASE_NAME, new ID2CIndexer()); |
| | | id2subtree = newIndex(ID2SUBTREE_DATABASE_NAME, new ID2SIndexer()); |
| | | } |
| | | |
| | | private Index newIndex(String name, Indexer indexer) |
| | | { |
| | | final Index index = new Index(databasePrefix + "_" + name, |
| | | indexer, state, config.getIndexEntryLimit(), 0, true, env, this); |
| | | index.open(); |
| | | if (!index.isTrusted()) |
| | | { |
| | | logger.info(NOTE_JEB_INDEX_ADD_REQUIRES_REBUILD, id2children.getName()); |
| | | logger.info(NOTE_JEB_INDEX_ADD_REQUIRES_REBUILD, index.getName()); |
| | | } |
| | | id2subtree = new Index(databasePrefix + "_" + ID2SUBTREE_DATABASE_NAME, |
| | | new ID2SIndexer(), state, config.getIndexEntryLimit(), 0, true, |
| | | env, this); |
| | | id2subtree.open(); |
| | | if (!id2subtree.isTrusted()) |
| | | { |
| | | logger.info(NOTE_JEB_INDEX_ADD_REQUIRES_REBUILD, id2subtree.getName()); |
| | | } |
| | | return index; |
| | | } |
| | | |
| | | |