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 legal-notices/CDDLv1_0.txt 010 * or http://forgerock.org/license/CDDLv1.0.html. 011 * See the License for the specific language governing permissions 012 * and limitations under the License. 013 * 014 * When distributing Covered Code, include this CDDL HEADER in each 015 * file and include the License file at legal-notices/CDDLv1_0.txt. 016 * If applicable, add the following below this CDDL HEADER, with the 017 * fields enclosed by brackets "[]" replaced with your own identifying 018 * information: 019 * Portions Copyright [yyyy] [name of copyright owner] 020 * 021 * CDDL HEADER END 022 * 023 * 024 * Copyright 2006-2010 Sun Microsystems, Inc. 025 * Portions Copyright 2012-2015 ForgeRock AS 026 */ 027package org.opends.server.backends.jeb; 028 029import static com.sleepycat.je.OperationStatus.*; 030 031import static org.opends.messages.BackendMessages.*; 032 033import java.util.*; 034 035import org.forgerock.i18n.slf4j.LocalizedLogger; 036import org.forgerock.opendj.ldap.ByteString; 037import org.forgerock.opendj.ldap.ConditionResult; 038import org.opends.server.backends.jeb.IndexBuffer.BufferedIndexValues; 039import org.opends.server.types.DirectoryException; 040import org.opends.server.types.Entry; 041import org.opends.server.types.Modification; 042import org.opends.server.util.StaticUtils; 043 044import com.sleepycat.je.*; 045 046/** 047 * Represents an index implemented by a JE database in which each key maps to 048 * a set of entry IDs. The key is a byte array, and is constructed from some 049 * normalized form of an attribute value (or fragment of a value) appearing 050 * in the entry. 051 */ 052public class Index extends DatabaseContainer 053{ 054 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 055 056 /** The indexer object to construct index keys from LDAP attribute values. */ 057 private Indexer indexer; 058 059 /** The limit on the number of entry IDs that may be indexed by one key. */ 060 private int indexEntryLimit; 061 /** 062 * Limit on the number of entry IDs that may be retrieved by cursoring 063 * through an index. 064 */ 065 private final int cursorEntryLimit; 066 /** 067 * Number of keys that have exceeded the entry limit since this 068 * object was created. 069 */ 070 private int entryLimitExceededCount; 071 072 /** The max number of tries to rewrite phantom records. */ 073 private final int phantomWriteRetries = 3; 074 075 /** 076 * Whether to maintain a count of IDs for a key once the entry limit 077 * has exceeded. 078 */ 079 private final boolean maintainCount; 080 081 private final State state; 082 083 /** 084 * A flag to indicate if this index should be trusted to be consistent 085 * with the entries database. If not trusted, we assume that existing 086 * entryIDSets for a key is still accurate. However, keys that do not 087 * exist are undefined instead of an empty entryIDSet. The following 088 * rules will be observed when the index is not trusted: 089 * 090 * - no entryIDs will be added to a non-existing key. 091 * - undefined entryIdSet will be returned whenever a key is not found. 092 */ 093 private boolean trusted; 094 095 private final ImportIDSet newImportIDSet; 096 097 /** 098 * Create a new index object. 099 * @param name The name of the index database within the entryContainer. 100 * @param indexer The indexer object to construct index keys from LDAP 101 * attribute values. 102 * @param state The state database to persist index state info. 103 * @param indexEntryLimit The configured limit on the number of entry IDs 104 * that may be indexed by one key. 105 * @param cursorEntryLimit The configured limit on the number of entry IDs 106 * @param maintainCount Whether to maintain a count of IDs for a key once 107 * the entry limit has exceeded. 108 * @param env The JE Environment 109 * @param entryContainer The database entryContainer holding this index. 110 * @throws DatabaseException If an error occurs in the JE database. 111 */ 112 @SuppressWarnings("unchecked") 113 Index(String name, Indexer indexer, State state, 114 int indexEntryLimit, int cursorEntryLimit, boolean maintainCount, 115 Environment env, EntryContainer entryContainer) 116 throws DatabaseException 117 { 118 super(name, env, entryContainer); 119 this.indexer = indexer; 120 this.indexEntryLimit = indexEntryLimit; 121 this.cursorEntryLimit = cursorEntryLimit; 122 this.maintainCount = maintainCount; 123 this.newImportIDSet = new ImportIDSet(indexEntryLimit, 124 indexEntryLimit, maintainCount); 125 126 this.dbConfig = JEBUtils.toDatabaseConfigNoDuplicates(env); 127 this.dbConfig.setOverrideBtreeComparator(true); 128 this.dbConfig.setBtreeComparator((Class<? extends Comparator<byte[]>>) 129 indexer.getComparator().getClass()); 130 131 this.state = state; 132 133 this.trusted = state.getIndexTrustState(null, this); 134 if (!trusted && entryContainer.getHighestEntryID().longValue() == 0) 135 { 136 // If there are no entries in the entry container then there 137 // is no reason why this index can't be upgraded to trusted. 138 setTrusted(null, true); 139 } 140 } 141 142 void indexEntry(Entry entry, Set<ByteString> keys) 143 { 144 indexer.indexEntry(entry, keys); 145 } 146 147 /** 148 * Add an add entry ID operation into a index buffer. 149 * 150 * @param buffer The index buffer to insert the ID into. 151 * @param keyBytes The index key bytes. 152 * @param entryID The entry ID. 153 */ 154 void insertID(IndexBuffer buffer, ByteString keyBytes, EntryID entryID) 155 { 156 getBufferedIndexValues(buffer, keyBytes).addEntryID(keyBytes, entryID); 157 } 158 159 /** 160 * Delete the specified import ID set from the import ID set associated with the key. 161 * 162 * @param key The key to delete the set from. 163 * @param importIdSet The import ID set to delete. 164 * @param data A database entry to use for data. 165 * @throws DatabaseException If a database error occurs. 166 */ 167 public void delete(DatabaseEntry key, ImportIDSet importIdSet, DatabaseEntry data) throws DatabaseException { 168 if (read(null, key, data, LockMode.DEFAULT) == SUCCESS) { 169 newImportIDSet.clear(); 170 newImportIDSet.remove(data.getData(), importIdSet); 171 if (newImportIDSet.isDefined() && newImportIDSet.size() == 0) 172 { 173 delete(null, key); 174 } 175 else 176 { 177 data.setData(newImportIDSet.toDatabase()); 178 put(null, key, data); 179 } 180 } else { 181 // Should never happen -- the keys should always be there. 182 throw new RuntimeException(); 183 } 184 } 185 186 /** 187 * Insert the specified import ID set into this index. Creates a DB cursor if needed. 188 * 189 * @param key The key to add the set to. 190 * @param importIdSet The set of import IDs. 191 * @param data Database entry to reuse for read. 192 * @throws DatabaseException If a database error occurs. 193 */ 194 public void insert(DatabaseEntry key, ImportIDSet importIdSet, DatabaseEntry data) throws DatabaseException { 195 final OperationStatus status = read(null, key, data, LockMode.DEFAULT); 196 if(status == OperationStatus.SUCCESS) { 197 newImportIDSet.clear(); 198 if (newImportIDSet.merge(data.getData(), importIdSet)) 199 { 200 entryLimitExceededCount++; 201 } 202 data.setData(newImportIDSet.toDatabase()); 203 put(null, key, data); 204 } else if(status == OperationStatus.NOTFOUND) { 205 if(!importIdSet.isDefined()) { 206 entryLimitExceededCount++; 207 } 208 data.setData(importIdSet.toDatabase()); 209 put(null, key, data); 210 } else { 211 // Should never happen during import. 212 throw new RuntimeException(); 213 } 214 } 215 216 /** 217 * Update the set of entry IDs for a given key. 218 * 219 * @param txn A database transaction, or null if none is required. 220 * @param key The database key. 221 * @param deletedIDs The IDs to remove for the key. 222 * @param addedIDs the IDs to add for the key. 223 * @throws DatabaseException If a database error occurs. 224 */ 225 void updateKey(Transaction txn, DatabaseEntry key, EntryIDSet deletedIDs, EntryIDSet addedIDs) 226 throws DatabaseException 227 { 228 DatabaseEntry data = new DatabaseEntry(); 229 230 if(deletedIDs == null && addedIDs == null) 231 { 232 final OperationStatus status = delete(txn, key); 233 if (status != SUCCESS && logger.isTraceEnabled()) 234 { 235 StringBuilder builder = new StringBuilder(); 236 StaticUtils.byteArrayToHexPlusAscii(builder, key.getData(), 4); 237 logger.trace("The expected key does not exist in the index %s.\nKey:%s ", name, builder); 238 } 239 return; 240 } 241 242 // Handle cases where nothing is changed early to avoid DB access. 243 if (isNullOrEmpty(deletedIDs) && isNullOrEmpty(addedIDs)) 244 { 245 return; 246 } 247 248 if(maintainCount) 249 { 250 for (int i = 0; i < phantomWriteRetries; i++) 251 { 252 if (updateKeyWithRMW(txn, key, data, deletedIDs, addedIDs) == SUCCESS) 253 { 254 return; 255 } 256 } 257 } 258 else 259 { 260 OperationStatus status = read(txn, key, data, LockMode.READ_COMMITTED); 261 if(status == OperationStatus.SUCCESS) 262 { 263 EntryIDSet entryIDList = new EntryIDSet(key.getData(), data.getData()); 264 if (entryIDList.isDefined()) 265 { 266 for (int i = 0; i < phantomWriteRetries; i++) 267 { 268 if (updateKeyWithRMW(txn, key, data, deletedIDs, addedIDs) == SUCCESS) 269 { 270 return; 271 } 272 } 273 } 274 } 275 else if (trusted) 276 { 277 if (deletedIDs != null) 278 { 279 logIndexCorruptError(txn, key); 280 } 281 282 if (isNotNullOrEmpty(addedIDs)) 283 { 284 data.setData(addedIDs.toDatabase()); 285 286 status = insert(txn, key, data); 287 if(status == OperationStatus.KEYEXIST) 288 { 289 for (int i = 1; i < phantomWriteRetries; i++) 290 { 291 if (updateKeyWithRMW(txn, key, data, deletedIDs, addedIDs) == SUCCESS) 292 { 293 return; 294 } 295 } 296 } 297 } 298 } 299 } 300 } 301 302 private boolean isNullOrEmpty(EntryIDSet entryIDSet) 303 { 304 return entryIDSet == null || entryIDSet.size() == 0; 305 } 306 307 private boolean isNotNullOrEmpty(EntryIDSet entryIDSet) 308 { 309 return entryIDSet != null && entryIDSet.size() > 0; 310 } 311 312 private OperationStatus updateKeyWithRMW(Transaction txn, 313 DatabaseEntry key, 314 DatabaseEntry data, 315 EntryIDSet deletedIDs, 316 EntryIDSet addedIDs) 317 throws DatabaseException 318 { 319 final OperationStatus status = read(txn, key, data, LockMode.RMW); 320 if(status == SUCCESS) 321 { 322 EntryIDSet entryIDList = computeEntryIDList(key, data, deletedIDs, addedIDs); 323 byte[] after = entryIDList.toDatabase(); 324 if (after != null) 325 { 326 data.setData(after); 327 return put(txn, key, data); 328 } 329 else 330 { 331 // No more IDs, so remove the key. If index is not 332 // trusted then this will cause all subsequent reads 333 // for this key to return undefined set. 334 return delete(txn, key); 335 } 336 } 337 else if (trusted) 338 { 339 if (deletedIDs != null) 340 { 341 logIndexCorruptError(txn, key); 342 } 343 344 if (isNotNullOrEmpty(addedIDs)) 345 { 346 data.setData(addedIDs.toDatabase()); 347 return insert(txn, key, data); 348 } 349 } 350 return OperationStatus.SUCCESS; 351 } 352 353 private EntryIDSet computeEntryIDList(DatabaseEntry key, DatabaseEntry data, EntryIDSet deletedIDs, 354 EntryIDSet addedIDs) 355 { 356 EntryIDSet entryIDList = new EntryIDSet(key.getData(), data.getData()); 357 if(addedIDs != null) 358 { 359 if(entryIDList.isDefined() && indexEntryLimit > 0) 360 { 361 long idCountDelta = addedIDs.size(); 362 if(deletedIDs != null) 363 { 364 idCountDelta -= deletedIDs.size(); 365 } 366 if(idCountDelta + entryIDList.size() >= indexEntryLimit) 367 { 368 if(maintainCount) 369 { 370 entryIDList = new EntryIDSet(entryIDList.size() + idCountDelta); 371 } 372 else 373 { 374 entryIDList = new EntryIDSet(); 375 } 376 entryLimitExceededCount++; 377 378 if(logger.isTraceEnabled()) 379 { 380 StringBuilder builder = new StringBuilder(); 381 StaticUtils.byteArrayToHexPlusAscii(builder, key.getData(), 4); 382 logger.trace("Index entry exceeded in index %s. " + 383 "Limit: %d. ID list size: %d.\nKey:%s", 384 name, indexEntryLimit, idCountDelta + addedIDs.size(), builder); 385 386 } 387 } 388 else 389 { 390 entryIDList.addAll(addedIDs); 391 if(deletedIDs != null) 392 { 393 entryIDList.deleteAll(deletedIDs); 394 } 395 } 396 } 397 else 398 { 399 entryIDList.addAll(addedIDs); 400 if(deletedIDs != null) 401 { 402 entryIDList.deleteAll(deletedIDs); 403 } 404 } 405 } 406 else if(deletedIDs != null) 407 { 408 entryIDList.deleteAll(deletedIDs); 409 } 410 return entryIDList; 411 } 412 413 /** 414 * Add an remove entry ID operation into a index buffer. 415 * 416 * @param buffer The index buffer to insert the ID into. 417 * @param keyBytes The index key bytes. 418 * @param entryID The entry ID. 419 */ 420 void removeID(IndexBuffer buffer, ByteString keyBytes, EntryID entryID) 421 { 422 getBufferedIndexValues(buffer, keyBytes).deleteEntryID(keyBytes, entryID); 423 } 424 425 private void logIndexCorruptError(Transaction txn, DatabaseEntry key) 426 { 427 if (logger.isTraceEnabled()) 428 { 429 StringBuilder builder = new StringBuilder(); 430 StaticUtils.byteArrayToHexPlusAscii(builder, key.getData(), 4); 431 logger.trace("The expected key does not exist in the index %s.\nKey:%s", name, builder); 432 } 433 434 setTrusted(txn, false); 435 logger.error(ERR_INDEX_CORRUPT_REQUIRES_REBUILD, name); 436 } 437 438 /** 439 * Buffered delete of a key from the JE database. 440 * @param buffer The index buffer to use to store the deleted keys 441 * @param keyBytes The index key bytes. 442 */ 443 public void delete(IndexBuffer buffer, ByteString keyBytes) 444 { 445 getBufferedIndexValues(buffer, keyBytes); 446 } 447 448 private BufferedIndexValues getBufferedIndexValues(IndexBuffer buffer, ByteString keyBytes) 449 { 450 return buffer.getBufferedIndexValues(this, keyBytes, indexer.getBSComparator()); 451 } 452 453 /** 454 * Check if an entry ID is in the set of IDs indexed by a given key. 455 * 456 * @param txn A database transaction, or null if none is required. 457 * @param key The index key. 458 * @param entryID The entry ID. 459 * @return true if the entry ID is indexed by the given key, 460 * false if it is not indexed by the given key, 461 * undefined if the key has exceeded the entry limit. 462 * @throws DatabaseException If an error occurs in the JE database. 463 */ 464 public ConditionResult containsID(Transaction txn, DatabaseEntry key, EntryID entryID) 465 throws DatabaseException 466 { 467 DatabaseEntry data = new DatabaseEntry(); 468 469 OperationStatus status = read(txn, key, data, LockMode.DEFAULT); 470 if (status == SUCCESS) 471 { 472 EntryIDSet entryIDList = new EntryIDSet(key.getData(), data.getData()); 473 if (!entryIDList.isDefined()) 474 { 475 return ConditionResult.UNDEFINED; 476 } 477 return ConditionResult.valueOf(entryIDList.contains(entryID)); 478 } 479 else if (trusted) 480 { 481 return ConditionResult.FALSE; 482 } 483 else 484 { 485 return ConditionResult.UNDEFINED; 486 } 487 } 488 489 /** 490 * Reads the set of entry IDs for a given key. 491 * 492 * @param key The database key. 493 * @param txn A database transaction, or null if none is required. 494 * @param lockMode The JE locking mode to be used for the database read. 495 * @return The entry IDs indexed by this key. 496 */ 497 public EntryIDSet readKey(DatabaseEntry key, Transaction txn, LockMode lockMode) 498 { 499 try 500 { 501 DatabaseEntry data = new DatabaseEntry(); 502 OperationStatus status = read( txn, key, data, lockMode); 503 if (status != SUCCESS) 504 { 505 if(trusted) 506 { 507 return new EntryIDSet(key.getData(), null); 508 } 509 else 510 { 511 return new EntryIDSet(); 512 } 513 } 514 return new EntryIDSet(key.getData(), data.getData()); 515 } 516 catch (DatabaseException e) 517 { 518 logger.traceException(e); 519 return new EntryIDSet(); 520 } 521 } 522 523 /** 524 * Writes the set of entry IDs for a given key. 525 * 526 * @param key The database key. 527 * @param entryIDList The entry IDs indexed by this key. 528 * @param txn A database transaction, or null if none is required. 529 * @throws DatabaseException If an error occurs in the JE database. 530 */ 531 public void writeKey(Transaction txn, DatabaseEntry key, EntryIDSet entryIDList) 532 throws DatabaseException 533 { 534 DatabaseEntry data = new DatabaseEntry(); 535 byte[] after = entryIDList.toDatabase(); 536 if (after != null) 537 { 538 if (!entryIDList.isDefined()) 539 { 540 entryLimitExceededCount++; 541 } 542 data.setData(after); 543 put(txn, key, data); 544 } 545 else 546 { 547 // No more IDs, so remove the key. 548 delete(txn, key); 549 } 550 } 551 552 /** 553 * Reads a range of keys and collects all their entry IDs into a 554 * single set. 555 * 556 * @param lower The lower bound of the range. A 0 length byte array indicates 557 * no lower bound and the range will start from the 558 * smallest key. 559 * @param upper The upper bound of the range. A 0 length byte array indicates 560 * no upper bound and the range will end at the largest 561 * key. 562 * @param lowerIncluded true if a key exactly matching the lower bound 563 * is included in the range, false if only keys 564 * strictly greater than the lower bound are included. 565 * This value is ignored if the lower bound is not 566 * specified. 567 * @param upperIncluded true if a key exactly matching the upper bound 568 * is included in the range, false if only keys 569 * strictly less than the upper bound are included. 570 * This value is ignored if the upper bound is not 571 * specified. 572 * @return The set of entry IDs. 573 */ 574 public EntryIDSet readRange(byte[] lower, byte[] upper, 575 boolean lowerIncluded, boolean upperIncluded) 576 { 577 // If this index is not trusted, then just return an undefined id set. 578 if (!trusted) 579 { 580 return new EntryIDSet(); 581 } 582 583 try 584 { 585 // Total number of IDs found so far. 586 int totalIDCount = 0; 587 LockMode lockMode = LockMode.DEFAULT; 588 589 DatabaseEntry data = new DatabaseEntry(); 590 DatabaseEntry key; 591 592 ArrayList<EntryIDSet> lists = new ArrayList<>(); 593 594 Cursor cursor = openCursor(null, CursorConfig.READ_COMMITTED); 595 try 596 { 597 final Comparator<byte[]> comparator = indexer.getComparator(); 598 OperationStatus status; 599 // Set the lower bound if necessary. 600 if(lower.length > 0) 601 { 602 key = new DatabaseEntry(lower); 603 604 // Initialize the cursor to the lower bound. 605 status = cursor.getSearchKeyRange(key, data, lockMode); 606 607 // Advance past the lower bound if necessary. 608 if (status == SUCCESS && !lowerIncluded && 609 comparator.compare(key.getData(), lower) == 0) 610 { 611 // Do not include the lower value. 612 status = cursor.getNext(key, data, lockMode); 613 } 614 } 615 else 616 { 617 key = new DatabaseEntry(); 618 status = cursor.getNext(key, data, lockMode); 619 } 620 621 if (status != OperationStatus.SUCCESS) 622 { 623 // There are no values. 624 return new EntryIDSet(key.getData(), null); 625 } 626 627 // Step through the keys until we hit the upper bound or the last key. 628 while (status == OperationStatus.SUCCESS) 629 { 630 // Check against the upper bound if necessary 631 if(upper.length > 0) 632 { 633 int cmp = comparator.compare(key.getData(), upper); 634 if (cmp > 0 || (cmp == 0 && !upperIncluded)) 635 { 636 break; 637 } 638 } 639 EntryIDSet list = new EntryIDSet(key.getData(), data.getData()); 640 if (!list.isDefined()) 641 { 642 // There is no point continuing. 643 return list; 644 } 645 totalIDCount += list.size(); 646 if (cursorEntryLimit > 0 && totalIDCount > cursorEntryLimit) 647 { 648 // There are too many. Give up and return an undefined list. 649 return new EntryIDSet(); 650 } 651 lists.add(list); 652 status = cursor.getNext(key, data, LockMode.DEFAULT); 653 } 654 655 return EntryIDSet.unionOfSets(lists, false); 656 } 657 finally 658 { 659 cursor.close(); 660 } 661 } 662 catch (DatabaseException e) 663 { 664 logger.traceException(e); 665 return new EntryIDSet(); 666 } 667 } 668 669 /** 670 * Get the number of keys that have exceeded the entry limit since this 671 * object was created. 672 * @return The number of keys that have exceeded the entry limit since this 673 * object was created. 674 */ 675 public int getEntryLimitExceededCount() 676 { 677 return entryLimitExceededCount; 678 } 679 680 /** 681 * Update the index buffer for a deleted entry. 682 * 683 * @param buffer The index buffer to use to store the deleted keys 684 * @param entryID The entry ID. 685 * @param entry The entry to be indexed. 686 * @throws DatabaseException If an error occurs in the JE database. 687 * @throws DirectoryException If a Directory Server error occurs. 688 */ 689 public void addEntry(IndexBuffer buffer, EntryID entryID, Entry entry) throws DatabaseException, DirectoryException 690 { 691 final Set<ByteString> addKeys = new HashSet<>(); 692 indexer.indexEntry(entry, addKeys); 693 694 for (ByteString keyBytes : addKeys) 695 { 696 insertID(buffer, keyBytes, entryID); 697 } 698 } 699 700 /** 701 * Update the index buffer for a deleted entry. 702 * 703 * @param buffer The index buffer to use to store the deleted keys 704 * @param entryID The entry ID 705 * @param entry The contents of the deleted entry. 706 * @throws DatabaseException If an error occurs in the JE database. 707 * @throws DirectoryException If a Directory Server error occurs. 708 */ 709 public void removeEntry(IndexBuffer buffer, EntryID entryID, Entry entry) 710 throws DatabaseException, DirectoryException 711 { 712 final Set<ByteString> delKeys = new HashSet<>(); 713 indexer.indexEntry(entry, delKeys); 714 715 for (ByteString keyBytes : delKeys) 716 { 717 removeID(buffer, keyBytes, entryID); 718 } 719 } 720 721 /** 722 * Update the index to reflect a sequence of modifications in a Modify 723 * operation. 724 * 725 * @param buffer The index buffer to use to store the deleted keys 726 * @param entryID The ID of the entry that was modified. 727 * @param oldEntry The entry before the modifications were applied. 728 * @param newEntry The entry after the modifications were applied. 729 * @param mods The sequence of modifications in the Modify operation. 730 * @throws DatabaseException If an error occurs in the JE database. 731 */ 732 public void modifyEntry(IndexBuffer buffer, 733 EntryID entryID, 734 Entry oldEntry, 735 Entry newEntry, 736 List<Modification> mods) 737 throws DatabaseException 738 { 739 final Map<ByteString, Boolean> modifiedKeys = new TreeMap<>(indexer.getBSComparator()); 740 indexer.modifyEntry(oldEntry, newEntry, mods, modifiedKeys); 741 742 for (Map.Entry<ByteString, Boolean> modifiedKey : modifiedKeys.entrySet()) 743 { 744 if(modifiedKey.getValue()) 745 { 746 insertID(buffer, modifiedKey.getKey(), entryID); 747 } 748 else 749 { 750 removeID(buffer, modifiedKey.getKey(), entryID); 751 } 752 } 753 } 754 755 /** 756 * Set the index entry limit. 757 * 758 * @param indexEntryLimit The index entry limit to set. 759 * @return True if a rebuild is required or false otherwise. 760 */ 761 public boolean setIndexEntryLimit(int indexEntryLimit) 762 { 763 final boolean rebuildRequired = 764 this.indexEntryLimit < indexEntryLimit && entryLimitExceededCount > 0; 765 this.indexEntryLimit = indexEntryLimit; 766 return rebuildRequired; 767 } 768 769 /** 770 * Set the indexer. 771 * 772 * @param indexer The indexer to set 773 */ 774 public void setIndexer(Indexer indexer) 775 { 776 this.indexer = indexer; 777 } 778 779 /** 780 * Return entry limit. 781 * 782 * @return The entry limit. 783 */ 784 public int getIndexEntryLimit() { 785 return this.indexEntryLimit; 786 } 787 788 /** 789 * Set the index trust state. 790 * @param txn A database transaction, or null if none is required. 791 * @param trusted True if this index should be trusted or false 792 * otherwise. 793 * @throws DatabaseException If an error occurs in the JE database. 794 */ 795 public synchronized void setTrusted(Transaction txn, boolean trusted) 796 throws DatabaseException 797 { 798 this.trusted = trusted; 799 state.putIndexTrustState(txn, this, trusted); 800 } 801 802 /** 803 * Return true iff this index is trusted. 804 * @return the trusted state of this index 805 */ 806 public synchronized boolean isTrusted() 807 { 808 return trusted; 809 } 810 811 /** 812 * Return <code>true</code> iff this index is being rebuilt. 813 * @return The rebuild state of this index 814 */ 815 public synchronized boolean isRebuildRunning() 816 { 817 return false; // FIXME inline? 818 } 819 820 /** 821 * Whether this index maintains a count of IDs for keys once the 822 * entry limit has exceeded. 823 * @return <code>true</code> if this index maintains court of IDs 824 * or <code>false</code> otherwise 825 */ 826 public boolean getMaintainCount() 827 { 828 return maintainCount; 829 } 830 831 /** 832 * Return an indexes comparator. 833 * 834 * @return The comparator related to an index. 835 */ 836 public Comparator<byte[]> getComparator() 837 { 838 return indexer.getComparator(); 839 } 840}