/* * 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 Sun Microsystems, Inc. */ package org.opends.guitools.controlpanel.task; import static org.opends.messages.AdminToolMessages.*; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; import javax.naming.NameNotFoundException; import javax.naming.NamingException; import javax.naming.ldap.InitialLdapContext; import javax.swing.SwingUtilities; import javax.swing.tree.TreePath; import org.opends.guitools.controlpanel.browser.BrowserController; import org.opends.guitools.controlpanel.datamodel.BackendDescriptor; import org.opends.guitools.controlpanel.datamodel.BaseDNDescriptor; import org.opends.guitools.controlpanel.datamodel.ControlPanelInfo; import org.opends.guitools.controlpanel.ui.ColorAndFontConstants; import org.opends.guitools.controlpanel.ui.ProgressDialog; import org.opends.guitools.controlpanel.ui.nodes.BasicNode; import org.opends.guitools.controlpanel.ui.nodes.BrowserNodeInfo; import org.opends.guitools.controlpanel.util.Utilities; import org.opends.messages.Message; import org.opends.server.types.DN; import org.opends.server.types.DirectoryException; import org.opends.server.util.cli.CommandBuilder; /** * The task that is launched when an entry must be deleted. */ public class DeleteEntryTask extends Task { private Set backendSet; private DN lastDn; private int nDeleted = 0; private int nToDelete = -1; private BrowserController controller; private TreePath[] paths; /** * Constructor of the task. * @param info the control panel information. * @param dlg the progress dialog where the task progress will be displayed. * @param paths the tree paths of the entries that must be deleted. * @param controller the Browser Controller. */ public DeleteEntryTask(ControlPanelInfo info, ProgressDialog dlg, TreePath[] paths, BrowserController controller) { super(info, dlg); backendSet = new HashSet(); this.controller = controller; this.paths = paths; SortedSet entries = new TreeSet(); boolean canPrecalculateNumberOfEntries = true; nToDelete = paths.length; for (TreePath path : paths) { BasicNode node = (BasicNode)path.getLastPathComponent(); /* if (node.getNumSubOrdinates() != -1) { nToDelete += node.getNumSubOrdinates(); } else if (node.isLeaf()) { canPrecalculateNumberOfEntries = false; } */ try { DN dn = DN.decode(node.getDN()); entries.add(dn); } catch (DirectoryException de) { throw new IllegalStateException("Unexpected error parsing dn: "+ node.getDN(), de); } } for (BackendDescriptor backend : info.getServerDescriptor().getBackends()) { for (BaseDNDescriptor baseDN : backend.getBaseDns()) { for (DN dn : entries) { if (dn.isDescendantOf(baseDN.getDn())) { backendSet.add(backend.getBackendID()); break; } } } } if (!canPrecalculateNumberOfEntries) { nToDelete = -1; } } /** * {@inheritDoc} */ public Type getType() { return Type.DELETE_ENTRY; } /** * {@inheritDoc} */ public Set getBackends() { return backendSet; } /** * {@inheritDoc} */ public Message getTaskDescription() { return INFO_CTRL_PANEL_DELETE_ENTRY_TASK_DESCRIPTION.get(); } /** * {@inheritDoc} */ protected String getCommandLinePath() { return null; } /** * {@inheritDoc} */ protected ArrayList getCommandLineArguments() { return new ArrayList(); } /** * {@inheritDoc} */ public boolean canLaunch(Task taskToBeLaunched, Collection incompatibilityReasons) { boolean canLaunch = true; if (!isServerRunning()) { if (state == State.RUNNING) { // All the operations are incompatible if they apply to this // backend for safety. This is a short operation so the limitation // has not a lot of impact. Set backends = new TreeSet(taskToBeLaunched.getBackends()); backends.retainAll(getBackends()); if (backends.size() > 0) { incompatibilityReasons.add(getIncompatibilityMessage(this, taskToBeLaunched)); canLaunch = false; } } } return canLaunch; } /** * {@inheritDoc} */ public boolean regenerateDescriptor() { return false; } /** * {@inheritDoc} */ public void runTask() { state = State.RUNNING; lastException = null; ArrayList alreadyDeleted = new ArrayList(); final ArrayList toNotify = new ArrayList(); int deletedSinceLastNotify = 0; try { for (TreePath path : paths) { BasicNode node = (BasicNode)path.getLastPathComponent(); try { DN dn = DN.decode(node.getDN()); boolean isDnDeleted = false; for (DN deletedDn : alreadyDeleted) { if (dn.isDescendantOf(deletedDn)) { isDnDeleted = true; break; } } if (!isDnDeleted) { InitialLdapContext ctx = controller.findConnectionForDisplayedEntry(node); deleteSubtree(ctx, dn); alreadyDeleted.add(dn); toNotify.add(controller.getNodeInfoFromPath(path)); deletedSinceLastNotify = nDeleted - deletedSinceLastNotify; if (deletedSinceLastNotify >= 10) { SwingUtilities.invokeAndWait(new Runnable() { public void run() { notifyEntriesDeleted(toNotify); toNotify.clear(); } }); } } } catch (DirectoryException de) { throw new IllegalStateException("Unexpected error parsing dn: "+ node.getDN(), de); } } if (toNotify.size() > 0) { SwingUtilities.invokeLater(new Runnable() { public void run() { notifyEntriesDeleted(toNotify); toNotify.clear(); } }); } state = State.FINISHED_SUCCESSFULLY; } catch (Throwable t) { lastException = t; state = State.FINISHED_WITH_ERROR; } if (nDeleted > 1) { getProgressDialog().appendProgressHtml(Utilities.applyFont( "
"+INFO_CTRL_PANEL_ENTRIES_DELETED.get(nDeleted), ColorAndFontConstants.progressFont)); } } /** * Notifies that some entries have been deleted. This will basically update * the browser controller so that the tree reflects the changes that have * been made. * @param deleteNodes the nodes that have been deleted. */ private void notifyEntriesDeleted(Collection deleteNodes) { TreePath pathToSelect = null; for (BrowserNodeInfo nodeInfo : deleteNodes) { TreePath parentPath = controller.notifyEntryDeleted(nodeInfo); if (pathToSelect != null) { if (parentPath.getPathCount() < pathToSelect.getPathCount()) { pathToSelect = parentPath; } } else { pathToSelect = parentPath; } } if (pathToSelect != null) { TreePath selectedPath = controller.getTree().getSelectionPath(); if (selectedPath == null) { controller.getTree().setSelectionPath(pathToSelect); } else if (!selectedPath.equals(pathToSelect) && (pathToSelect.getPathCount() < selectedPath.getPathCount())) { controller.getTree().setSelectionPath(pathToSelect); } } } /** * Deletes a subtree. * @param ctx the connection to the server. * @param dnToRemove the DN of the subtree to delete. * @throws NamingException if an error occurs deleting the subtree. */ private void deleteSubtree(InitialLdapContext ctx, DN dnToRemove) throws NamingException { lastDn = dnToRemove; try { SwingUtilities.invokeLater(new Runnable() { public void run() { printEquivalentCommandToDelete(lastDn); getProgressDialog().setSummary( Message.raw( Utilities.applyFont( INFO_CTRL_PANEL_DELETING_ENTRY_SUMMARY.get( lastDn.toString()).toString(), ColorAndFontConstants.defaultFont))); } }); Utilities.deleteSubtree(ctx, dnToRemove); nDeleted ++; if ((nToDelete > 0) && (nToDelete > nDeleted)) { SwingUtilities.invokeLater(new Runnable() { public void run() { getProgressDialog().getProgressBar().setIndeterminate(false); getProgressDialog().getProgressBar().setValue( (100 * nDeleted) / nToDelete); } }); } } catch (NameNotFoundException nnfe) { // The entry is not there: it has been removed } } /* private void deleteSubtree(DirContext ctx, DN dnToRemove) throws NamingException, DirectoryException { lastDn = dnToRemove; try { SearchControls ctls = new SearchControls(); ctls.setSearchScope(SearchControls.ONELEVEL_SCOPE); String filter = "(|(objectClass=*)(objectclass=ldapsubentry))"; ctls.setReturningAttributes(new String[] {"dn"}); NamingEnumeration entryDNs = ctx.search(Utilities.getJNDIName(dnToRemove.toString()), filter, ctls); DN entryDNFound = dnToRemove; while (entryDNs.hasMore()) { SearchResult sr = entryDNs.next(); if (!sr.getName().equals("")) { CustomSearchResult res = new CustomSearchResult(sr, dnToRemove.toString()); entryDNFound = DN.decode(res.getDN()); deleteSubtree(ctx,entryDNFound); } } } catch (NameNotFoundException nnfe) { // The entry is not there: it has been removed } try { if (((nDeleted % 10) == 0) || (nDeleted == 0)) { SwingUtilities.invokeLater(new Runnable() { public void run() { getProgressDialog().setSummary( Utilities.applyFont("Deleting entry '"+lastDn+"'...", ColorAndFontConstants.defaultFont)); if (nDeleted == 0) { // Just give an example printEquivalentCommandToDelete(lastDn); } } }); } ctx.destroySubcontext(Utilities.getJNDIName(dnToRemove.toString())); nDeleted ++; if (((nDeleted % 10) == 0) && (nToDelete > 0) && (nToDelete > nDeleted)) { SwingUtilities.invokeLater(new Runnable() { public void run() { getProgressDialog().getProgressBar().setIndeterminate(false); getProgressDialog().getProgressBar().setValue( (100 * nDeleted) / nToDelete); } }); } } catch (NameNotFoundException nnfe) { // The entry is not there: it has been removed } } private void printEquivalentCommandToDelete(DN dn) { ArrayList args = new ArrayList(); args.add(getCommandLineName("ldapdelete")); args.addAll(getObfuscatedCommandLineArguments( getConnectionCommandLineArguments())); args.add(dn.toString()); StringBuilder sb = new StringBuilder(); for (String arg : args) { sb.append(" "+CommandBuilder.escapeValue(arg)); } getProgressDialog().appendProgressHtml(Utilities.applyFont( "Equivalent command line to delete entry '"+dn+"':
"+ sb.toString()+"

", ColorAndFontConstants.progressFont)); } */ /** * Prints in the progress dialog the equivalent command-line to delete a * subtree. * @param dn the DN of the subtree to be deleted. */ private void printEquivalentCommandToDelete(DN dn) { ArrayList args = new ArrayList(); args.add(getCommandLinePath("ldapdelete")); args.addAll(getObfuscatedCommandLineArguments( getConnectionCommandLineArguments())); args.add("-J"); args.add(Utilities.SUBTREE_CTRL_OID); args.add(dn.toString()); StringBuilder sb = new StringBuilder(); for (String arg : args) { sb.append(" "+CommandBuilder.escapeValue(arg)); } getProgressDialog().appendProgressHtml(Utilities.applyFont( INFO_CTRL_PANEL_EQUIVALENT_CMD_TO_DELETE_ENTRY.get(dn.toString())+ "
"+ sb.toString()+"

", ColorAndFontConstants.progressFont)); } }