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 2006-2008 Sun Microsystems, Inc.
026     */
027    package org.opends.server.backends.jeb;
028    import org.opends.messages.Message;
029    
030    import org.opends.server.api.DirectoryThread;
031    
032    import com.sleepycat.je.*;
033    
034    import org.opends.server.types.*;
035    
036    import static org.opends.messages.JebMessages.
037        ERR_JEB_MISSING_DN2ID_RECORD;
038    import static org.opends.messages.JebMessages.
039        ERR_JEB_REBUILD_INDEX_FAILED;
040    import static org.opends.messages.JebMessages.
041        ERR_JEB_REBUILD_INSERT_ENTRY_FAILED;
042    import static org.opends.server.loggers.ErrorLogger.logError;
043    import static org.opends.server.loggers.debug.DebugLogger.*;
044    import org.opends.server.loggers.debug.DebugTracer;
045    import static org.opends.server.util.StaticUtils.stackTraceToSingleLineString;
046    
047    
048    /**
049     * A thread to do the actual work of rebuilding an index.
050     */
051    public class IndexRebuildThread extends DirectoryThread
052    {
053      /**
054       * The tracer object for the debug logger.
055       */
056      private static final DebugTracer TRACER = getTracer();
057    
058      /**
059       * The entry container.
060       */
061      EntryContainer ec = null;
062    
063      /**
064       * The internal database/indexType to rebuild.
065       */
066      IndexType indexType = null;
067    
068      /**
069       * The attribute indexType to rebuild.
070       */
071      AttributeIndex attrIndex = null;
072    
073      /**
074       * The VLV index to rebuild.
075       */
076      VLVIndex vlvIndex = null;
077    
078      /**
079       * The indexType to rebuild.
080       */
081      Index index = null;
082    
083      /**
084       * The ID2ENTRY database.
085       */
086      ID2Entry id2entry = null;
087    
088      /**
089       * The number of total entries to rebuild. An negative value indicates this
090       * value is not yet known.
091       */
092      long totalEntries = -1;
093    
094      /**
095       * The number of entries processed.
096       */
097      long processedEntries = 0;
098    
099      /**
100       * The number of entries rebuilt successfully.
101       */
102      long rebuiltEntries = 0;
103    
104      /**
105       * The number of entries rebuilt with possible duplicates.
106       */
107      long duplicatedEntries = 0;
108    
109      /**
110       * The number of entries that were skipped because they were not applicable
111       * for the indexType or because an error occurred.
112       */
113      long skippedEntries = 0;
114    
115      /**
116       * The types of internal indexes that are rebuildable.
117       */
118      enum IndexType
119      {
120        DN2ID, DN2URI, ID2CHILDREN, ID2SUBTREE, INDEX, ATTRIBUTEINDEX, VLVINDEX
121      }
122    
123      /**
124       * Construct a new index rebuild thread to rebuild a system index.
125       *
126       * @param ec The entry container to rebuild in.
127       * @param index The index type to rebuild.
128       */
129      IndexRebuildThread(EntryContainer ec, IndexType index)
130      {
131        super("Index Rebuild Thread " + ec.getDatabasePrefix() + "_" +
132            index.toString());
133        this.ec = ec;
134        this.indexType = index;
135        this.id2entry = ec.getID2Entry();
136      }
137    
138      /**
139       * Construct a new index rebuild thread to rebuild an index.
140       *
141       * @param ec The entry container to rebuild in.
142       * @param index The index to rebuild.
143       */
144      IndexRebuildThread(EntryContainer ec, Index index)
145      {
146        super("Index Rebuild Thread " + index.getName());
147        this.ec = ec;
148        this.indexType = IndexType.INDEX;
149        this.index = index;
150        this.id2entry = ec.getID2Entry();
151      }
152    
153      /**
154       * Construct a new index rebuild thread to rebuild an attribute index.
155       *
156       * @param ec The entry container to rebuild in.
157       * @param index The attribute index to rebuild.
158       */
159      IndexRebuildThread(EntryContainer ec, AttributeIndex index)
160      {
161        super("Index Rebuild Thread " + index.getName());
162        this.ec = ec;
163        this.indexType = IndexType.ATTRIBUTEINDEX;
164        this.attrIndex = index;
165        this.id2entry = ec.getID2Entry();
166      }
167    
168      /**
169       * Construct a new index rebuild thread to rebuild an VLV index.
170       *
171       * @param ec The entry container to rebuild in.
172       * @param vlvIndex The VLV index to rebuild.
173       */
174      IndexRebuildThread(EntryContainer ec, VLVIndex vlvIndex)
175      {
176        super("Index Rebuild Thread " + vlvIndex.getName());
177        this.ec = ec;
178        this.indexType = IndexType.VLVINDEX;
179        this.vlvIndex = vlvIndex;
180        this.id2entry = ec.getID2Entry();
181      }
182    
183      /**
184       * Clear the database and prep it for the rebuild.
185       *
186       * @throws DatabaseException if a JE databse error occurs while clearing
187       * the database being rebuilt.
188       */
189      public void clearDatabase() throws DatabaseException
190      {
191        if(indexType == null)
192        {
193          //TODO: throw error
194          if(debugEnabled())
195          {
196            TRACER.debugError("No index type specified. Rebuild process " +
197                "terminated.");
198          }
199    
200          return;
201        }
202        if(indexType == IndexType.ATTRIBUTEINDEX && attrIndex == null)
203        {
204          //TODO: throw error
205          if(debugEnabled())
206          {
207            TRACER.debugError("No attribute index specified. Rebuild process " +
208                "terminated.");
209          }
210    
211          return;
212        }
213    
214        if(indexType == IndexType.INDEX && index == null)
215        {
216          //TODO: throw error
217          if(debugEnabled())
218          {
219            TRACER.debugError("No index specified. Rebuild process terminated.");
220          }
221    
222          return;
223        }
224    
225        if(indexType == IndexType.VLVINDEX && vlvIndex == null)
226        {
227          //TODO: throw error
228          if(debugEnabled())
229          {
230            TRACER.debugError("No VLV index specified. Rebuild process " +
231                "terminated.");
232          }
233    
234          return;
235        }
236    
237        switch(indexType)
238        {
239          case DN2ID :
240            ec.clearDatabase(ec.getDN2ID());
241            break;
242          case DN2URI :
243            ec.clearDatabase(ec.getDN2URI());
244            break;
245          case ID2CHILDREN :
246            ec.clearDatabase(ec.getID2Children());
247            ec.getID2Children().setRebuildStatus(true);
248            break;
249          case ID2SUBTREE :
250            ec.clearDatabase(ec.getID2Subtree());
251            ec.getID2Subtree().setRebuildStatus(true);
252            break;
253          case ATTRIBUTEINDEX :
254            ec.clearAttributeIndex(attrIndex);
255            attrIndex.setRebuildStatus(true);
256            break;
257          case VLVINDEX :
258            ec.clearDatabase(vlvIndex);
259            vlvIndex.setRebuildStatus(true);
260            break;
261          case INDEX :
262            ec.clearDatabase(index);
263            index.setRebuildStatus(true);
264        }
265      }
266    
267      /**
268       * Start the rebuild process.
269       */
270      public void run()
271      {
272        if(indexType == null)
273        {
274          //TODO: throw error
275          if(debugEnabled())
276          {
277            TRACER.debugError("No index type specified. Rebuild process " +
278                "terminated.");
279          }
280    
281          return;
282        }
283        if(indexType == IndexType.ATTRIBUTEINDEX && attrIndex == null)
284        {
285          //TODO: throw error
286          if(debugEnabled())
287          {
288            TRACER.debugError("No attribute index specified. Rebuild process " +
289                "terminated.");
290          }
291    
292          return;
293        }
294    
295        if(indexType == IndexType.INDEX && index == null)
296        {
297          //TODO: throw error
298          if(debugEnabled())
299          {
300            TRACER.debugError("No index specified. Rebuild process terminated.");
301          }
302    
303          return;
304        }
305    
306        if(indexType == IndexType.VLVINDEX && vlvIndex == null)
307        {
308          //TODO: throw error
309          if(debugEnabled())
310          {
311            TRACER.debugError("No VLV index specified. Rebuild process " +
312                "terminated.");
313          }
314    
315          return;
316        }
317    
318        try
319        {
320          totalEntries = getTotalEntries();
321    
322          switch(indexType)
323          {
324            case DN2ID : rebuildDN2ID();
325              break;
326            case DN2URI : rebuildDN2URI();
327              break;
328            case ID2CHILDREN : rebuildID2Children();
329              break;
330            case ID2SUBTREE : rebuildID2Subtree();
331              break;
332            case ATTRIBUTEINDEX : rebuildAttributeIndex(attrIndex);
333              break;
334            case VLVINDEX : rebuildVLVIndex(vlvIndex);
335              break;
336            case INDEX : rebuildAttributeIndex(index);
337          }
338    
339          if(debugEnabled())
340          {
341            TRACER.debugVerbose("Rebuilt %d entries", rebuiltEntries);
342          }
343        }
344        catch(Exception e)
345        {
346          Message message = ERR_JEB_REBUILD_INDEX_FAILED.get(
347              this.getName(), stackTraceToSingleLineString(e));
348          logError(message);
349    
350          if(debugEnabled())
351          {
352            TRACER.debugCaught(DebugLogLevel.ERROR, e);
353          }
354        }
355      }
356    
357      /**
358       * Rebuild an interal DN2ID database.
359       *
360       * @throws DatabaseException If an error occurs during the rebuild.
361       */
362      private void rebuildDN2ID() throws DatabaseException
363      {
364        DN2ID dn2id = ec.getDN2ID();
365    
366        if(debugEnabled())
367        {
368          TRACER.debugInfo("Initiating rebuild of the %s database",
369                           dn2id.getName());
370          TRACER.debugVerbose("%d entries will be rebuilt", totalEntries);
371        }
372    
373    
374        //Iterate through the id2entry database and insert associated dn2id
375        //records.
376        Cursor cursor = id2entry.openCursor(null, CursorConfig.READ_COMMITTED);
377        try
378        {
379          DatabaseEntry key = new DatabaseEntry();
380          DatabaseEntry data = new DatabaseEntry();
381          LockMode lockMode = LockMode.DEFAULT;
382    
383          OperationStatus status;
384          for (status = cursor.getFirst(key, data, lockMode);
385               status == OperationStatus.SUCCESS;
386               status = cursor.getNext(key, data, lockMode))
387          {
388            Transaction txn = ec.beginTransaction();
389            try
390            {
391              EntryID entryID = new EntryID(key);
392              Entry entry = JebFormat.entryFromDatabase(data.getData(),
393                                 ec.getRootContainer().getCompressedSchema());
394    
395              // Insert into dn2id.
396              if (dn2id.insert(txn, entry.getDN(), entryID))
397              {
398                rebuiltEntries++;
399              }
400              else
401              {
402                // The entry ID already exists in the database.
403                // This could happen if some other process got to this entry
404                // before we did. Since the backend should be offline, this
405                // might be a problem.
406                duplicatedEntries++;
407                if(debugEnabled())
408                {
409                  TRACER.debugInfo("Unable to insert entry with DN %s and ID %d " +
410                      "into the DN2ID database because it already exists.",
411                            entry.getDN().toString(), entryID.longValue());
412                }
413              }
414              EntryContainer.transactionCommit(txn);
415              processedEntries++;
416            }
417            catch (Exception e)
418            {
419              EntryContainer.transactionAbort(txn);
420              skippedEntries++;
421    
422              Message message = ERR_JEB_REBUILD_INSERT_ENTRY_FAILED.get(
423                  dn2id.getName(), stackTraceToSingleLineString(e));
424              logError(message);
425    
426              if (debugEnabled())
427              {
428                TRACER.debugCaught(DebugLogLevel.ERROR, e);
429              }
430            }
431          }
432        }
433        finally
434        {
435          cursor.close();
436        }
437      }
438    
439      /**
440       * Rebuild the ID2URI internal database.
441       *
442       * @throws DatabaseException if an error occurs during rebuild.
443       */
444      private void rebuildDN2URI() throws DatabaseException
445      {
446        DN2URI dn2uri = ec.getDN2URI();
447    
448        if(debugEnabled())
449        {
450          TRACER.debugInfo("Initiating rebuild of the %s database",
451                           dn2uri.getName());
452          TRACER.debugVerbose("%d entries will be rebuilt", totalEntries);
453        }
454    
455    
456        //Iterate through the id2entry database and insert associated dn2uri
457        //records.
458        Cursor cursor = id2entry.openCursor(null, CursorConfig.READ_COMMITTED);
459        try
460        {
461          DatabaseEntry key = new DatabaseEntry();
462          DatabaseEntry data = new DatabaseEntry();
463          LockMode lockMode = LockMode.DEFAULT;
464    
465    
466          OperationStatus status;
467          for (status = cursor.getFirst(key, data, lockMode);
468               status == OperationStatus.SUCCESS;
469               status = cursor.getNext(key, data, lockMode))
470          {
471            Transaction txn = ec.beginTransaction();
472            try
473            {
474              EntryID entryID = new EntryID(key);
475              Entry entry = JebFormat.entryFromDatabase(data.getData(),
476                                 ec.getRootContainer().getCompressedSchema());
477    
478              // Insert into dn2uri.
479              if (dn2uri.addEntry(txn, entry))
480              {
481                rebuiltEntries++;
482              }
483              else
484              {
485                // The entry DN and URIs already exists in the database.
486                // This could happen if some other process got to this entry
487                // before we did. Since the backend should be offline, this
488                // might be a problem.
489                duplicatedEntries++;
490                if(debugEnabled())
491                {
492                  TRACER.debugInfo("Unable to insert entry with DN %s and ID %d " +
493                      "into the DN2URI database because it already exists.",
494                            entry.getDN().toString(), entryID.longValue());
495                }
496              }
497              EntryContainer.transactionCommit(txn);
498              processedEntries++;
499            }
500            catch (Exception e)
501            {
502              EntryContainer.transactionAbort(txn);
503              skippedEntries++;
504    
505              Message message = ERR_JEB_REBUILD_INSERT_ENTRY_FAILED.get(
506                  dn2uri.getName(), stackTraceToSingleLineString(e));
507              logError(message);
508    
509              if (debugEnabled())
510              {
511                TRACER.debugCaught(DebugLogLevel.ERROR, e);
512              }
513            }
514          }
515        }
516        finally
517        {
518          cursor.close();
519        }
520      }
521    
522      /**
523       * Rebuild the ID2Subtree internal index. This depends on the DN2ID and DN2URI
524       * databases being complete.
525       *
526       * @throws DatabaseException if an error occurs during rebuild.
527       */
528      private void rebuildID2Children() throws DatabaseException
529      {
530        Index id2children = ec.getID2Children();
531    
532        if(debugEnabled())
533        {
534          TRACER.debugInfo("Initiating rebuild of the %s index",
535                           id2children.getName());
536          TRACER.debugVerbose("%d entries will be rebuilt", totalEntries);
537        }
538    
539    
540        DN2ID dn2id = ec.getDN2ID();
541        DN2URI dn2uri = ec.getDN2URI();
542    
543        //Iterate through the id2entry database and insert associated dn2children
544        //records.
545        Cursor cursor = id2entry.openCursor(null, CursorConfig.READ_COMMITTED);
546        try
547        {
548          DatabaseEntry key = new DatabaseEntry();
549          DatabaseEntry data = new DatabaseEntry();
550          LockMode lockMode = LockMode.DEFAULT;
551    
552          OperationStatus status;
553          for (status = cursor.getFirst(key, data, lockMode);
554               status == OperationStatus.SUCCESS;
555               status = cursor.getNext(key, data, lockMode))
556          {
557            Transaction txn = ec.beginTransaction();
558            try
559            {
560              EntryID entryID = new EntryID(key);
561              Entry entry = JebFormat.entryFromDatabase(data.getData(),
562                                 ec.getRootContainer().getCompressedSchema());
563    
564              // Check that the parent entry exists.
565              DN parentDN = ec.getParentWithinBase(entry.getDN());
566              if (parentDN != null)
567              {
568                // Check for referral entries above the target.
569                dn2uri.targetEntryReferrals(entry.getDN(), null);
570    
571                // Read the parent ID from dn2id.
572                EntryID parentID = dn2id.get(txn, parentDN, LockMode.DEFAULT);
573                if (parentID != null)
574                {
575                  // Insert into id2children for parent ID.
576                  if(id2children.insertID(txn, parentID.getDatabaseEntry(),
577                                          entryID))
578                  {
579                    rebuiltEntries++;
580                  }
581                  else
582                  {
583                    // The entry already exists in the database.
584                    // This could happen if some other process got to this entry
585                    // before we did. Since the backend should be offline, this
586                    // might be a problem.
587                    if(debugEnabled())
588                    {
589                      duplicatedEntries++;
590                      TRACER.debugInfo("Unable to insert entry with DN %s and " +
591                          "ID %d into the DN2Subtree database because it already " +
592                          "exists.",
593                                entry.getDN().toString(), entryID.longValue());
594                    }
595                  }
596                }
597                else
598                {
599                  Message msg = ERR_JEB_MISSING_DN2ID_RECORD.get(
600                      parentDN.toNormalizedString());
601                  throw new JebException(msg);
602                }
603              }
604              else
605              {
606                skippedEntries++;
607              }
608              EntryContainer.transactionCommit(txn);
609              processedEntries++;
610            }
611            catch (Exception e)
612            {
613              EntryContainer.transactionAbort(txn);
614              skippedEntries++;
615    
616              Message message = ERR_JEB_REBUILD_INSERT_ENTRY_FAILED.get(
617                  id2children.getName(), stackTraceToSingleLineString(e));
618              logError(message);
619    
620              if (debugEnabled())
621              {
622                TRACER.debugCaught(DebugLogLevel.ERROR, e);
623              }
624            }
625          }
626          id2children.setRebuildStatus(false);
627          id2children.setTrusted(null, true);
628        }
629        finally
630        {
631          cursor.close();
632        }
633      }
634    
635      /**
636       * Rebuild the ID2Subtree internal index. This depends on the DN2ID and DN2URI
637       * databases being complete.
638       *
639       * @throws DatabaseException if an error occurs during rebuild.
640       */
641      private void rebuildID2Subtree() throws DatabaseException
642      {
643        Index id2subtree = ec.getID2Subtree();
644    
645        if(debugEnabled())
646        {
647          TRACER.debugInfo("Initiating rebuild of the %s index",
648                           id2subtree.getName());
649          TRACER.debugVerbose("%d entries will be rebuilt", totalEntries);
650        }
651    
652    
653        DN2ID dn2id = ec.getDN2ID();
654        DN2URI dn2uri = ec.getDN2URI();
655    
656        //Iterate through the id2entry database and insert associated dn2subtree
657        //records.
658        Cursor cursor = id2entry.openCursor(null, CursorConfig.READ_COMMITTED);
659        try
660        {
661          DatabaseEntry key = new DatabaseEntry();
662          DatabaseEntry data = new DatabaseEntry();
663          LockMode lockMode = LockMode.DEFAULT;
664    
665          OperationStatus status;
666          for (status = cursor.getFirst(key, data, lockMode);
667               status == OperationStatus.SUCCESS;
668               status = cursor.getNext(key, data, lockMode))
669          {
670            Transaction txn = ec.beginTransaction();
671            try
672            {
673              EntryID entryID = new EntryID(key);
674              Entry entry = JebFormat.entryFromDatabase(data.getData(),
675                                 ec.getRootContainer().getCompressedSchema());
676    
677              // Check that the parent entry exists.
678              DN parentDN = ec.getParentWithinBase(entry.getDN());
679              if (parentDN != null)
680              {
681                boolean success = true;
682    
683                // Check for referral entries above the target.
684                dn2uri.targetEntryReferrals(entry.getDN(), null);
685    
686                // Read the parent ID from dn2id.
687                EntryID parentID = dn2id.get(txn, parentDN, LockMode.DEFAULT);
688                if (parentID != null)
689                {
690                  // Insert into id2subtree for parent ID.
691                  if(!id2subtree.insertID(txn, parentID.getDatabaseEntry(),
692                                          entryID))
693                  {
694                    success = false;
695                  }
696    
697                  // Iterate up through the superior entries, starting above the
698                  // parent.
699                  for (DN dn = ec.getParentWithinBase(parentDN); dn != null;
700                       dn = ec.getParentWithinBase(dn))
701                  {
702                    // Read the ID from dn2id.
703                    EntryID nodeID = dn2id.get(null, dn, LockMode.DEFAULT);
704                    if (nodeID != null)
705                    {
706                      // Insert into id2subtree for this node.
707                      if(!id2subtree.insertID(null, nodeID.getDatabaseEntry(),
708                                              entryID))
709                      {
710                        success = false;
711                      }
712                    }
713                    else
714                    {
715                      Message msg =
716                          ERR_JEB_MISSING_DN2ID_RECORD.get(dn.toNormalizedString());
717                      throw new JebException(msg);
718                    }
719                  }
720                }
721                else
722                {
723                  Message msg = ERR_JEB_MISSING_DN2ID_RECORD.get(
724                      parentDN.toNormalizedString());
725                  throw new JebException(msg);
726                }
727    
728                if(success)
729                {
730                  rebuiltEntries++;
731                }
732                else
733                {
734                  // The entry already exists in the database.
735                  // This could happen if some other process got to this entry
736                  // before we did. Since the backend should be offline, this
737                  // might be a problem.
738                  if(debugEnabled())
739                  {
740                    duplicatedEntries++;
741                    TRACER.debugInfo("Unable to insert entry with DN %s and ID " +
742                        "%d into the DN2Subtree database because it already " +
743                        "exists.", entry.getDN().toString(), entryID.longValue());
744                  }
745                }
746              }
747              else
748              {
749                skippedEntries++;
750              }
751              EntryContainer.transactionCommit(txn);
752              processedEntries++;
753            }
754            catch (Exception e)
755            {
756              EntryContainer.transactionAbort(txn);
757              skippedEntries++;
758    
759              Message message = ERR_JEB_REBUILD_INSERT_ENTRY_FAILED.get(
760                  id2subtree.getName(), stackTraceToSingleLineString(e));
761              logError(message);
762    
763              if (debugEnabled())
764              {
765                TRACER.debugCaught(DebugLogLevel.ERROR, e);
766              }
767            }
768          }
769          id2subtree.setRebuildStatus(false);
770          id2subtree.setTrusted(null, true);
771        }
772        finally
773        {
774          cursor.close();
775        }
776      }
777    
778      /**
779       * Rebuild the attribute index.
780       *
781       * @param index The indexType to rebuild.
782       * @throws DatabaseException if an error occurs during rebuild.
783       */
784      private void rebuildAttributeIndex(AttributeIndex index)
785          throws DatabaseException
786      {
787        if(debugEnabled())
788        {
789          TRACER.debugInfo("Initiating rebuild of the %s index",
790                           index.getName());
791          TRACER.debugVerbose("%d entries will be rebuilt", totalEntries);
792        }
793    
794        //Iterate through the id2entry database and insert associated indexType
795        //records.
796        Cursor cursor = id2entry.openCursor(null, CursorConfig.READ_COMMITTED);
797        try
798        {
799          DatabaseEntry key = new DatabaseEntry();
800          DatabaseEntry data = new DatabaseEntry();
801          LockMode lockMode = LockMode.DEFAULT;
802    
803          OperationStatus status;
804          for (status = cursor.getFirst(key, data, lockMode);
805               status == OperationStatus.SUCCESS;
806               status = cursor.getNext(key, data, lockMode))
807          {
808            Transaction txn = ec.beginTransaction();
809            try
810            {
811              EntryID entryID = new EntryID(key);
812              Entry entry = JebFormat.entryFromDatabase(data.getData(),
813                                 ec.getRootContainer().getCompressedSchema());
814    
815              // Insert into attribute indexType.
816              if(index.addEntry(txn, entryID, entry))
817              {
818                rebuiltEntries++;
819              }
820              else
821              {
822                // The entry already exists in one or more entry sets.
823                // This could happen if some other process got to this entry
824                // before we did. Since the backend should be offline, this
825                // might be a problem.
826                if(debugEnabled())
827                {
828                  duplicatedEntries++;
829                  TRACER.debugInfo("Unable to insert entry with DN %s and ID %d " +
830                      "into the DN2Subtree database because it already " +
831                      "exists.",
832                            entry.getDN().toString(), entryID.longValue());
833                }
834              }
835              EntryContainer.transactionCommit(txn);
836              processedEntries++;
837            }
838            catch (Exception e)
839            {
840              EntryContainer.transactionAbort(txn);
841              skippedEntries++;
842    
843              Message message = ERR_JEB_REBUILD_INSERT_ENTRY_FAILED.get(
844                  index.getName(), stackTraceToSingleLineString(e));
845              logError(message);
846    
847              if (debugEnabled())
848              {
849                TRACER.debugCaught(DebugLogLevel.ERROR, e);
850              }
851            }
852          }
853          index.setRebuildStatus(false);
854          index.setTrusted(null, true);
855        }
856        finally
857        {
858          cursor.close();
859        }
860      }
861    
862      /**
863       * Rebuild the VLV index.
864       *
865       * @param vlvIndex The VLV index to rebuild.
866       * @throws DatabaseException if an error occurs during rebuild.
867       */
868      private void rebuildVLVIndex(VLVIndex vlvIndex)
869          throws DatabaseException
870      {
871    
872        //Iterate through the id2entry database and insert associated indexType
873        //records.
874        Cursor cursor = id2entry.openCursor(null, CursorConfig.READ_COMMITTED);
875        try
876        {
877          DatabaseEntry key = new DatabaseEntry();
878          DatabaseEntry data = new DatabaseEntry();
879          LockMode lockMode = LockMode.DEFAULT;
880    
881          OperationStatus status;
882          for (status = cursor.getFirst(key, data, lockMode);
883               status == OperationStatus.SUCCESS;
884               status = cursor.getNext(key, data, lockMode))
885          {
886            Transaction txn = ec.beginTransaction();
887            try
888            {
889              EntryID entryID = new EntryID(key);
890              Entry entry = JebFormat.entryFromDatabase(data.getData(),
891                                 ec.getRootContainer().getCompressedSchema());
892    
893              // Insert into attribute indexType.
894              if(vlvIndex.addEntry(txn, entryID, entry))
895              {
896                rebuiltEntries++;
897              }
898              else
899              {
900                // The entry already exists in one or more entry sets.
901                // This could happen if some other process got to this entry
902                // before we did. Since the backend should be offline, this
903                // might be a problem.
904                if(debugEnabled())
905                {
906                  duplicatedEntries++;
907                  TRACER.debugInfo("Unable to insert entry with DN %s and ID %d " +
908                      "into the VLV index %s because it already " +
909                      "exists.",
910                            entry.getDN().toString(), entryID.longValue(),
911                            vlvIndex.getName());
912                }
913              }
914    
915              EntryContainer.transactionCommit(txn);
916              processedEntries++;
917            }
918            catch (Exception e)
919            {
920              EntryContainer.transactionAbort(txn);
921              skippedEntries++;
922    
923              Message message = ERR_JEB_REBUILD_INSERT_ENTRY_FAILED.get(
924                  vlvIndex.getName(), stackTraceToSingleLineString(e));
925              logError(message);
926    
927              if (debugEnabled())
928              {
929                TRACER.debugCaught(DebugLogLevel.ERROR, e);
930              }
931            }
932          }
933          vlvIndex.setRebuildStatus(false);
934          vlvIndex.setTrusted(null, true);
935        }
936        finally
937        {
938          cursor.close();
939        }
940      }
941    
942      /**
943       * Rebuild the partial attribute index.
944       *
945       * @param index The indexType to rebuild.
946       * @throws DatabaseException if an error occurs during rebuild.
947       */
948      private void rebuildAttributeIndex(Index index)
949          throws DatabaseException
950      {
951        if(debugEnabled())
952        {
953          TRACER.debugInfo("Initiating rebuild of the %s attribute index",
954                           index.getName());
955          TRACER.debugVerbose("%d entries will be rebuilt", totalEntries);
956        }
957    
958        //Iterate through the id2entry database and insert associated indexType
959        //records.
960        Cursor cursor = id2entry.openCursor(null, CursorConfig.READ_COMMITTED);
961        try
962        {
963          DatabaseEntry key = new DatabaseEntry();
964          DatabaseEntry data = new DatabaseEntry();
965          LockMode lockMode = LockMode.DEFAULT;
966    
967          OperationStatus status;
968          for (status = cursor.getFirst(key, data, lockMode);
969               status == OperationStatus.SUCCESS;
970               status = cursor.getNext(key, data, lockMode))
971          {
972            Transaction txn = ec.beginTransaction();
973            try
974            {
975              EntryID entryID = new EntryID(key);
976              Entry entry = JebFormat.entryFromDatabase(data.getData(),
977                                 ec.getRootContainer().getCompressedSchema());
978    
979              // Insert into attribute indexType.
980              if(index.addEntry(txn, entryID, entry))
981              {
982                rebuiltEntries++;
983              }
984              else
985              {
986                // The entry already exists in one or more entry sets.
987                // This could happen if some other process got to this entry
988                // before we did. Since the backend should be offline, this
989                // might be a problem.
990                if(debugEnabled())
991                {
992                  duplicatedEntries++;
993                  TRACER.debugInfo("Unable to insert entry with DN %s and ID %d " +
994                      "into the DN2Subtree database because it already " +
995                      "exists.",
996                            entry.getDN().toString(), entryID.longValue());
997                }
998              }
999              EntryContainer.transactionCommit(txn);
1000              processedEntries++;
1001            }
1002            catch (Exception e)
1003            {
1004              EntryContainer.transactionAbort(txn);
1005              skippedEntries++;
1006    
1007              Message message = ERR_JEB_REBUILD_INSERT_ENTRY_FAILED.get(
1008                  index.getName(), stackTraceToSingleLineString(e));
1009              logError(message);
1010    
1011              if (debugEnabled())
1012              {
1013                TRACER.debugCaught(DebugLogLevel.ERROR, e);
1014              }
1015            }
1016          }
1017          index.setRebuildStatus(false);
1018          index.setTrusted(null, true);
1019        }
1020        finally
1021        {
1022          cursor.close();
1023        }
1024      }
1025    
1026      /**
1027       * Get the total entries to process in the rebuild.
1028       *
1029       * @return The total entries to process.
1030       * @throws DatabaseException if an error occurs while getting the total
1031       *         number of entries to process.
1032       */
1033      public long getTotalEntries() throws DatabaseException
1034      {
1035        //If total entries is not calculated yet, do it now.
1036        if(totalEntries < 0)
1037        {
1038          totalEntries = id2entry.getRecordCount();
1039        }
1040        return totalEntries;
1041      }
1042    
1043      /**
1044       * Get the number of entries processed in the rebuild.
1045       *
1046       * @return The total entries processed.
1047       */
1048      public long getProcessedEntries()
1049      {
1050        return processedEntries;
1051      }
1052    
1053      /**
1054       * Get the number of entries successfully rebuilt.
1055       *
1056       * @return The number of entries successfully rebuilt.
1057       */
1058      public long getRebuiltEntries()
1059      {
1060        return rebuiltEntries;
1061      }
1062    
1063      /**
1064       * Get the number of entries that encountered duplicated indexType values in
1065       * the rebuild process.
1066       *
1067       * @return The number of entries that encountered duplicated indexType values
1068       *         in the rebuild process.
1069       */
1070      public long getDuplicatedEntries()
1071      {
1072        return duplicatedEntries;
1073      }
1074    
1075      /**
1076       * Get the number of entries skipped because they were either not applicable
1077       * or an error occurred during the process.
1078       *
1079       * @return The number of entries skipped.
1080       */
1081      public long getSkippedEntries()
1082      {
1083        return skippedEntries;
1084      }
1085    
1086      /**
1087       * Get the index type being rebuilt by this thread.
1088       *
1089       * @return The index type being rebuilt by this thread.
1090       */
1091      public IndexType getIndexType()
1092      {
1093        return indexType;
1094      }
1095    }
1096    
1097