From e651d8a4ba69ea4764d780ccf66068bd6f9c9c46 Mon Sep 17 00:00:00 2001
From: jvergara <jvergara@localhost>
Date: Tue, 04 Nov 2008 17:47:26 +0000
Subject: [PATCH] Fix for issue 3554 (Control Panel : Error displayed after deleting several entries).

---
 opends/src/guitools/org/opends/guitools/controlpanel/task/DeleteEntryTask.java |  288 +++++++++++++++++++++++++++++++++------------------------
 1 files changed, 166 insertions(+), 122 deletions(-)

diff --git a/opends/src/guitools/org/opends/guitools/controlpanel/task/DeleteEntryTask.java b/opends/src/guitools/org/opends/guitools/controlpanel/task/DeleteEntryTask.java
index b16f599..13f8433 100644
--- a/opends/src/guitools/org/opends/guitools/controlpanel/task/DeleteEntryTask.java
+++ b/opends/src/guitools/org/opends/guitools/controlpanel/task/DeleteEntryTask.java
@@ -32,20 +32,28 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Set;
 import java.util.SortedSet;
 import java.util.TreeSet;
 
 import javax.naming.NameNotFoundException;
+import javax.naming.NamingEnumeration;
 import javax.naming.NamingException;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+import javax.naming.ldap.BasicControl;
+import javax.naming.ldap.Control;
 import javax.naming.ldap.InitialLdapContext;
 import javax.swing.SwingUtilities;
 import javax.swing.tree.TreePath;
 
+import org.opends.admin.ads.util.ConnectionUtils;
 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.datamodel.CustomSearchResult;
 import org.opends.guitools.controlpanel.ui.ColorAndFontConstants;
 import org.opends.guitools.controlpanel.ui.ProgressDialog;
 import org.opends.guitools.controlpanel.ui.nodes.BasicNode;
@@ -67,6 +75,9 @@
   private int nToDelete = -1;
   private BrowserController controller;
   private TreePath[] paths;
+  private long lastProgressTime;
+  private boolean equivalentCommandWithControlPrinted = false;
+  private boolean equivalentCommandWithoutControlPrinted = false;
 
   /**
    * Constructor of the task.
@@ -88,16 +99,6 @@
     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());
@@ -182,8 +183,7 @@
       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.
+        // backend for safety.
         Set<String> backends =
           new TreeSet<String>(taskToBeLaunched.getBackends());
         backends.retainAll(getBackends());
@@ -215,9 +215,7 @@
     lastException = null;
 
     ArrayList<DN> alreadyDeleted = new ArrayList<DN>();
-    final ArrayList<BrowserNodeInfo> toNotify =
-      new ArrayList<BrowserNodeInfo>();
-    int deletedSinceLastNotify = 0;
+    ArrayList<BrowserNodeInfo> toNotify = new ArrayList<BrowserNodeInfo>();
     try
     {
       for (TreePath path : paths)
@@ -239,21 +237,15 @@
           {
             InitialLdapContext ctx =
               controller.findConnectionForDisplayedEntry(node);
-            deleteSubtree(ctx, dn);
-            alreadyDeleted.add(dn);
-            toNotify.add(controller.getNodeInfoFromPath(path));
-            deletedSinceLastNotify = nDeleted - deletedSinceLastNotify;
-            if (deletedSinceLastNotify >= 10)
+            if (node.getNumSubOrdinates() > 40)
             {
-              SwingUtilities.invokeAndWait(new Runnable()
-              {
-                public void run()
-                {
-                  notifyEntriesDeleted(toNotify);
-                  toNotify.clear();
-                }
-              });
+              deleteSubtreeWithControl(ctx, dn, path, toNotify);
             }
+            else
+            {
+              deleteSubtreeRecursively(ctx, dn, path, toNotify);
+            }
+            alreadyDeleted.add(dn);
           }
         }
         catch (DirectoryException de)
@@ -264,12 +256,14 @@
       }
       if (toNotify.size() > 0)
       {
+        final List<BrowserNodeInfo> fToNotify =
+          new ArrayList<BrowserNodeInfo>(toNotify);
+        toNotify.clear();
         SwingUtilities.invokeLater(new Runnable()
         {
           public void run()
           {
-            notifyEntriesDeleted(toNotify);
-            toNotify.clear();
+            notifyEntriesDeleted(fToNotify);
           }
         });
       }
@@ -292,12 +286,12 @@
    * 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.
+   * @param deletedNodes the nodes that have been deleted.
    */
-  private void notifyEntriesDeleted(Collection<BrowserNodeInfo> deleteNodes)
+  private void notifyEntriesDeleted(Collection<BrowserNodeInfo> deletedNodes)
   {
     TreePath pathToSelect = null;
-    for (BrowserNodeInfo nodeInfo : deleteNodes)
+    for (BrowserNodeInfo nodeInfo : deletedNodes)
     {
       TreePath parentPath = controller.notifyEntryDeleted(nodeInfo);
       if (pathToSelect != null)
@@ -327,64 +321,48 @@
     }
   }
 
-  /**
-   * 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)
+  private void deleteSubtreeRecursively(InitialLdapContext ctx, DN dnToRemove,
+      TreePath path, ArrayList<BrowserNodeInfo> toNotify)
   throws NamingException, DirectoryException
   {
     lastDn = dnToRemove;
 
-    try {
+    long t = System.currentTimeMillis();
+    boolean displayProgress =
+      (((nDeleted % 20) == 0) || ((t - lastProgressTime) > 5000))  &&
+      (nToDelete > 0) && (nToDelete > nDeleted);
+
+    if (displayProgress)
+    {
+      // Only display the first entry equivalent command-line.
+      SwingUtilities.invokeLater(new Runnable()
+      {
+        public void run()
+        {
+          if (!equivalentCommandWithoutControlPrinted)
+          {
+            printEquivalentCommandToDelete(lastDn, false);
+            equivalentCommandWithoutControlPrinted = true;
+          }
+          getProgressDialog().setSummary(
+              Message.raw(
+                  Utilities.applyFont(
+                      INFO_CTRL_PANEL_DELETING_ENTRY_SUMMARY.get(
+                          lastDn.toString()).toString(),
+                          ColorAndFontConstants.defaultFont)));
+        }
+      });
+    }
+
+    try
+    {
       SearchControls ctls = new SearchControls();
       ctls.setSearchScope(SearchControls.ONELEVEL_SCOPE);
       String filter =
-      "(|(objectClass=*)(objectclass=ldapsubentry))";
+        "(|(objectClass=*)(objectclass=ldapsubentry))";
       ctls.setReturningAttributes(new String[] {"dn"});
       NamingEnumeration<SearchResult> entryDNs =
-      ctx.search(Utilities.getJNDIName(dnToRemove.toString()), filter, ctls);
+        ctx.search(Utilities.getJNDIName(dnToRemove.toString()), filter, ctls);
 
       DN entryDNFound = dnToRemove;
       while (entryDNs.hasMore())
@@ -393,9 +371,9 @@
         if (!sr.getName().equals(""))
         {
           CustomSearchResult res =
-           new CustomSearchResult(sr, dnToRemove.toString());
+            new CustomSearchResult(sr, dnToRemove.toString());
           entryDNFound = DN.decode(res.getDN());
-          deleteSubtree(ctx,entryDNFound);
+          deleteSubtreeRecursively(ctx, entryDNFound, null, toNotify);
         }
       }
 
@@ -405,27 +383,25 @@
 
     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))
+      if (path != null)
       {
+        toNotify.add(controller.getNodeInfoFromPath(path));
+      }
+      nDeleted ++;
+      if (displayProgress)
+      {
+        lastProgressTime = t;
+        final Collection<BrowserNodeInfo> fToNotify;
+        if (toNotify.size() > 0)
+        {
+          fToNotify = new ArrayList<BrowserNodeInfo>(toNotify);
+          toNotify.clear();
+        }
+        else
+        {
+          fToNotify = null;
+        }
         SwingUtilities.invokeLater(new Runnable()
         {
           public void run()
@@ -433,46 +409,114 @@
             getProgressDialog().getProgressBar().setIndeterminate(false);
             getProgressDialog().getProgressBar().setValue(
                 (100 * nDeleted) / nToDelete);
+            if (fToNotify != null)
+            {
+              notifyEntriesDeleted(fToNotify);
+            }
           }
         });
       }
-    } catch (NameNotFoundException nnfe) {
+    } catch (NameNotFoundException nnfe)
+    {
       // The entry is not there: it has been removed
     }
   }
 
-  private void printEquivalentCommandToDelete(DN dn)
+  private void deleteSubtreeWithControl(InitialLdapContext ctx, DN dn,
+      TreePath path, ArrayList<BrowserNodeInfo> toNotify)
+  throws NamingException
   {
-    ArrayList<String> args = new ArrayList<String>();
-    args.add(getCommandLineName("ldapdelete"));
-    args.addAll(getObfuscatedCommandLineArguments(
-        getConnectionCommandLineArguments()));
-    args.add(dn.toString());
-    StringBuilder sb = new StringBuilder();
-    for (String arg : args)
+    lastDn = dn;
+    long t = System.currentTimeMillis();
+    //  Only display the first entry equivalent command-line.
+    SwingUtilities.invokeLater(new Runnable()
     {
-      sb.append(" "+CommandBuilder.escapeValue(arg));
+      public void run()
+      {
+        if (!equivalentCommandWithControlPrinted)
+        {
+          printEquivalentCommandToDelete(lastDn, true);
+          equivalentCommandWithControlPrinted = true;
+        }
+        getProgressDialog().setSummary(
+            Message.raw(
+                Utilities.applyFont(
+                    INFO_CTRL_PANEL_DELETING_ENTRY_SUMMARY.get(
+                        lastDn.toString()).toString(),
+                        ColorAndFontConstants.defaultFont)));
+      }
+    });
+    //  Use a copy of the dir context since we are using an specific
+    // control to delete the subtree and this can cause
+    // synchronization problems when the tree is refreshed.
+    InitialLdapContext ctx1 = null;
+    try
+    {
+      ctx1 = ConnectionUtils.cloneInitialLdapContext(ctx,
+          ConnectionUtils.getDefaultLDAPTimeout(),
+          getInfo().getTrustManager(), null);
+      Control[] ctls = {new BasicControl(Utilities.SUBTREE_CTRL_OID)};
+      ctx1.setRequestControls(ctls);
+      ctx1.destroySubcontext(Utilities.getJNDIName(dn.toString()));
     }
-
-    getProgressDialog().appendProgressHtml(Utilities.applyFont(
-        "Equivalent command line to delete entry '"+dn+"':<br><b>"+
-        sb.toString()+"</b><br><br>",
-        ColorAndFontConstants.progressFont));
+    finally
+    {
+      try
+      {
+        ctx1.close();
+      }
+      catch (Throwable th)
+      {
+      }
+    }
+    nDeleted ++;
+    lastProgressTime = t;
+    if (path != null)
+    {
+      toNotify.add(controller.getNodeInfoFromPath(path));
+    }
+    final Collection<BrowserNodeInfo> fToNotify;
+    if (toNotify.size() > 0)
+    {
+      fToNotify = new ArrayList<BrowserNodeInfo>(toNotify);
+      toNotify.clear();
+    }
+    else
+    {
+      fToNotify = null;
+    }
+    SwingUtilities.invokeLater(new Runnable()
+    {
+      public void run()
+      {
+        getProgressDialog().getProgressBar().setIndeterminate(false);
+        getProgressDialog().getProgressBar().setValue(
+            (100 * nDeleted) / nToDelete);
+        if (fToNotify != null)
+        {
+          notifyEntriesDeleted(fToNotify);
+        }
+      }
+    });
   }
-*/
+
   /**
    * Prints in the progress dialog the equivalent command-line to delete a
    * subtree.
    * @param dn the DN of the subtree to be deleted.
+   * @param usingControl whether we must include the control or not.
    */
-  private void printEquivalentCommandToDelete(DN dn)
+  private void printEquivalentCommandToDelete(DN dn, boolean usingControl)
   {
     ArrayList<String> args = new ArrayList<String>();
     args.add(getCommandLinePath("ldapdelete"));
     args.addAll(getObfuscatedCommandLineArguments(
         getConnectionCommandLineArguments()));
-    args.add("-J");
-    args.add(Utilities.SUBTREE_CTRL_OID);
+    if (usingControl)
+    {
+      args.add("-J");
+      args.add(Utilities.SUBTREE_CTRL_OID);
+    }
     args.add(dn.toString());
     StringBuilder sb = new StringBuilder();
     for (String arg : args)

--
Gitblit v1.10.0