/* * 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 * trunk/opends/resource/legal-notices/OpenDS.LICENSE * or https://OpenDS.dev.java.net/OpenDS.LICENSE. * 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 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, * add the following below this CDDL HEADER, with the fields enclosed * by brackets "[]" replaced with your own identifying information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * * Copyright 2008-2009 Sun Microsystems, Inc. */ package org.opends.server.backends.ndb; import com.mysql.cluster.ndbj.NdbApiException; import com.mysql.cluster.ndbj.NdbOperation; import org.opends.messages.Message; import org.opends.server.types.DN; import org.opends.server.types.DirectoryException; import org.opends.server.types.Entry; import org.opends.server.types.LDIFExportConfig; import org.opends.server.util.LDIFException; import java.io.IOException; import java.util.*; import org.opends.server.backends.ndb.OperationContainer.DN2IDSearchCursor; import org.opends.server.backends.ndb.OperationContainer.SearchCursorResult; import org.opends.server.types.DebugLogLevel; import static org.opends.server.loggers.ErrorLogger.logError; import static org.opends.server.loggers.debug.DebugLogger.*; import org.opends.server.loggers.debug.DebugTracer; import static org.opends.messages.NdbMessages.*; /** * Export a NDB backend to LDIF. */ public class ExportJob { /** * The tracer object for the debug logger. */ private static final DebugTracer TRACER = getTracer(); /** * The requested LDIF export configuration. */ private LDIFExportConfig exportConfig; /** * The number of milliseconds between job progress reports. */ private long progressInterval = 10000; /** * The current number of entries exported. */ private long exportedCount = 0; /** * The current number of entries skipped. */ private long skippedCount = 0; /** * Create a new export job. * * @param exportConfig The requested LDIF export configuration. */ public ExportJob(LDIFExportConfig exportConfig) { this.exportConfig = exportConfig; } /** * Export entries from the backend to an LDIF file. * @param rootContainer The root container to export. * @throws NdbApiException If an error occurs in the NDB database. * @throws IOException If an I/O error occurs while writing an entry. * @throws LDIFException If an error occurs while trying to determine whether * to write an entry. */ public void exportLDIF(RootContainer rootContainer) throws IOException, LDIFException, NdbApiException { List includeBranches = exportConfig.getIncludeBranches(); DN baseDN; ArrayList exportContainers = new ArrayList(); for (EntryContainer entryContainer : rootContainer.getEntryContainers()) { // Skip containers that are not covered by the include branches. baseDN = entryContainer.getBaseDN(); if (includeBranches == null || includeBranches.isEmpty()) { exportContainers.add(entryContainer); } else { for (DN includeBranch : includeBranches) { if (includeBranch.isDescendantOf(baseDN) || includeBranch.isAncestorOf(baseDN)) { exportContainers.add(entryContainer); } } } } // Make a note of the time we started. long startTime = System.currentTimeMillis(); // Start a timer for the progress report. Timer timer = new Timer(); TimerTask progressTask = new ProgressTask(); timer.scheduleAtFixedRate(progressTask, progressInterval, progressInterval); // Iterate through the containers. try { for (EntryContainer exportContainer : exportContainers) { if (exportConfig.isCancelled()) { break; } exportContainer.sharedLock.lock(); try { exportContainer(exportContainer); } finally { exportContainer.sharedLock.unlock(); } } } finally { timer.cancel(); } long finishTime = System.currentTimeMillis(); long totalTime = (finishTime - startTime); float rate = 0; if (totalTime > 0) { rate = 1000f*exportedCount / totalTime; } Message message = NOTE_NDB_EXPORT_FINAL_STATUS.get( exportedCount, skippedCount, totalTime/1000, rate); logError(message); } /** * Export the entries in a single entry entryContainer, in other words from * one of the base DNs. * @param entryContainer The entry container that holds the entries to be * exported. * @throws NdbApiException If an error occurs in the NDB database. * @throws IOException If an error occurs while writing an entry. * @throws LDIFException If an error occurs while trying to determine * whether to write an entry. */ private void exportContainer(EntryContainer entryContainer) throws IOException, LDIFException, NdbApiException { OperationContainer dn2id = entryContainer.getDN2ID(); RootContainer rc = entryContainer.getRootContainer(); DN baseDN = DN.NULL_DN; AbstractTransaction txn = new AbstractTransaction(rc); DN2IDSearchCursor cursor = dn2id.getSearchCursor(txn, baseDN); cursor.open(); try { SearchCursorResult result = cursor.getNext(); while (result != null) { if (exportConfig.isCancelled()) { break; } DN dn = null; try { dn = DN.decode(result.dn); } catch (DirectoryException ex) { if (debugEnabled()) { TRACER.debugCaught(DebugLogLevel.ERROR, ex); } skippedCount++; continue; } Entry entry = null; AbstractTransaction leafTxn = new AbstractTransaction(rc); try { entry = dn2id.get(leafTxn, dn, NdbOperation.LockMode.LM_CommittedRead); } finally { if (leafTxn != null) { leafTxn.close(); } } if ((entry != null) && entry.toLDIF(exportConfig)) { exportedCount++; } else { skippedCount++; } // Move to the next record. result = cursor.getNext(); } } finally { cursor.close(); if (txn != null) { txn.close(); } } } /** * This class reports progress of the export job at fixed intervals. */ class ProgressTask extends TimerTask { /** * The number of entries that had been exported at the time of the * previous progress report. */ private long previousCount = 0; /** * The time in milliseconds of the previous progress report. */ private long previousTime; /** * Create a new export progress task. */ public ProgressTask() { previousTime = System.currentTimeMillis(); } /** * The action to be performed by this timer task. */ public void run() { long latestCount = exportedCount; long deltaCount = (latestCount - previousCount); long latestTime = System.currentTimeMillis(); long deltaTime = latestTime - previousTime; if (deltaTime == 0) { return; } float rate = 1000f*deltaCount / deltaTime; Message message = NOTE_NDB_EXPORT_PROGRESS_REPORT.get(latestCount, skippedCount, rate); logError(message); previousCount = latestCount; previousTime = latestTime; } }; }