From 08db26a180ec0958cbc04ef4aa33debcacb3d5ed Mon Sep 17 00:00:00 2001
From: jvergara <jvergara@localhost>
Date: Thu, 22 Oct 2009 13:31:32 +0000
Subject: [PATCH] Control Panel fix for issue 4302 (Unexpected errors using single JNDI connection with SSL and multiple threads.)

---
 opendj-sdk/opends/src/guitools/org/opends/guitools/controlpanel/ui/BrowseEntriesPanel.java      |   98 ++++++++++++++++++-----
 opendj-sdk/opends/src/guitools/org/opends/guitools/controlpanel/util/LDAPEntryReader.java       |   26 ++++++
 opendj-sdk/opends/src/guitools/org/opends/guitools/controlpanel/datamodel/ControlPanelInfo.java |  115 +++++++++++++++-------------
 3 files changed, 162 insertions(+), 77 deletions(-)

diff --git a/opendj-sdk/opends/src/guitools/org/opends/guitools/controlpanel/datamodel/ControlPanelInfo.java b/opendj-sdk/opends/src/guitools/org/opends/guitools/controlpanel/datamodel/ControlPanelInfo.java
index af2c227..c1cb3c8 100644
--- a/opendj-sdk/opends/src/guitools/org/opends/guitools/controlpanel/datamodel/ControlPanelInfo.java
+++ b/opendj-sdk/opends/src/guitools/org/opends/guitools/controlpanel/datamodel/ControlPanelInfo.java
@@ -605,70 +605,48 @@
         {
           reader = createNewConfigFromDirContextReader();
           ((ConfigFromDirContext)reader).readConfiguration(ctx);
-          if (reader.getExceptions().size() > 0)
+
+          boolean connectionWorks = checkConnections(ctx, userDataCtx);
+          if (!connectionWorks)
           {
-            // Check the connection
-            boolean connectionWorks = false;
-            int nMaxErrors = 5;
-            for (int i=0; i< nMaxErrors && !connectionWorks; i++)
+            if (isLocal)
             {
-              try
-              {
-                Utilities.pingDirContext(ctx);
-                connectionWorks = true;
-              }
-              catch (NamingException ne)
-              {
-                try
-                {
-                  Thread.sleep(400);
-                }
-                catch (Throwable t)
-                {
-                }
-              }
+              // Try with off-line info
+              reader = createNewConfigFromFileReader();
+              ((ConfigFromFile)reader).readConfiguration();
             }
-            if (!connectionWorks)
+            else
             {
-              if (isLocal)
-              {
-                // Try with offline info
-                reader = createNewConfigFromFileReader();
-                ((ConfigFromFile)reader).readConfiguration();
-              }
-              else
-              {
-                desc.setStatus(
-                    ServerDescriptor.ServerStatus.NOT_CONNECTED_TO_REMOTE);
-                reader = null;
-              }
+              desc.setStatus(
+                  ServerDescriptor.ServerStatus.NOT_CONNECTED_TO_REMOTE);
+              reader = null;
+            }
+            try
+            {
+              ctx.close();
+            }
+            catch (Throwable t)
+            {
+            }
+            this.ctx = null;
+            if (connectionPool.isConnectionRegistered(userDataCtx))
+            {
               try
               {
-                ctx.close();
+                connectionPool.unregisterConnection(userDataCtx);
               }
               catch (Throwable t)
               {
               }
-              this.ctx = null;
-              if (connectionPool.isConnectionRegistered(userDataCtx))
-              {
-                try
-                {
-                  connectionPool.unregisterConnection(userDataCtx);
-                }
-                catch (Throwable t)
-                {
-                }
-              }
-              try
-              {
-                userDataCtx.close();
-              }
-              catch (Throwable t)
-              {
-              }
-              userDataCtx = null;
             }
+            try
+            {
+              userDataCtx.close();
+            }
+            catch (Throwable t)
+            {
+            }
+            userDataCtx = null;
           }
         }
       }
@@ -1341,4 +1319,35 @@
     }
     return isRunningOnServer;
   }
+
+  private boolean checkConnections(InitialLdapContext ctx,
+      InitialLdapContext userCtx)
+  {
+    // Check the connection
+    boolean connectionWorks = false;
+    int nMaxErrors = 5;
+    for (int i=0; i< nMaxErrors && !connectionWorks; i++)
+    {
+      try
+      {
+        Utilities.pingDirContext(ctx);
+        if (userCtx != null)
+        {
+          Utilities.pingDirContext(userCtx);
+        }
+        connectionWorks = true;
+      }
+      catch (NamingException ne)
+      {
+        try
+        {
+          Thread.sleep(400);
+        }
+        catch (Throwable t)
+        {
+        }
+      }
+    }
+    return connectionWorks;
+  }
 }
diff --git a/opendj-sdk/opends/src/guitools/org/opends/guitools/controlpanel/ui/BrowseEntriesPanel.java b/opendj-sdk/opends/src/guitools/org/opends/guitools/controlpanel/ui/BrowseEntriesPanel.java
index ff8ec76..9277f3e 100644
--- a/opendj-sdk/opends/src/guitools/org/opends/guitools/controlpanel/ui/BrowseEntriesPanel.java
+++ b/opendj-sdk/opends/src/guitools/org/opends/guitools/controlpanel/ui/BrowseEntriesPanel.java
@@ -67,11 +67,13 @@
 import javax.swing.JSeparator;
 import javax.swing.JSplitPane;
 import javax.swing.JTree;
+import javax.swing.SwingUtilities;
 import javax.swing.event.TreeSelectionEvent;
 import javax.swing.event.TreeSelectionListener;
 import javax.swing.tree.TreePath;
 
 import org.opends.guitools.controlpanel.datamodel.ControlPanelInfo;
+import org.opends.guitools.controlpanel.datamodel.CustomSearchResult;
 import org.opends.guitools.controlpanel.datamodel.ServerDescriptor;
 import org.opends.guitools.controlpanel.event.ConfigurationChangeEvent;
 import org.opends.guitools.controlpanel.event.EntryReadErrorEvent;
@@ -149,8 +151,9 @@
 
   private boolean ignoreTreeSelectionEvents = false;
 
-  private ArrayList<LDAPEntryReader> entryReaderQueue =
-    new ArrayList<LDAPEntryReader>();
+  private LDAPEntryReader entryReader;
+
+  private Thread entryReaderThread;
 
   /**
    * {@inheritDoc}
@@ -523,12 +526,11 @@
           controller.findConnectionForDisplayedEntry(node);
         LDAPEntryReader reader = new LDAPEntryReader(dn, ctx);
         reader.addEntryReadListener(entryPane);
-        cleanupReaderQueue();
         // Required to update the browser controller properly if the entry is
         // deleted.
         entryPane.setTreePath(path);
-        reader.startBackgroundTask();
-        entryReaderQueue.add(reader);
+        stopCurrentReader();
+        startReader(reader);
       }
       catch (Throwable t)
       {
@@ -541,7 +543,7 @@
     }
     else
     {
-      cleanupReaderQueue();
+      stopCurrentReader();
       if ((paths != null) && (paths.length > 1))
       {
         entryPane.multipleEntriesSelected();
@@ -553,26 +555,76 @@
     }
   }
 
-  /**
-   * Cleans up the reader queue (the queue where we store the entries that we
-   * must read).
-   *
-   */
-  private void cleanupReaderQueue()
+  private void stopCurrentReader()
   {
-    ArrayList<LDAPEntryReader> toRemove = new ArrayList<LDAPEntryReader>();
-    for (LDAPEntryReader r : entryReaderQueue)
+    if (entryReader != null)
     {
-      if (r.isOver())
-      {
-        toRemove.add(r);
-      }
-      else
-      {
-        r.interrupt();
-      }
+      entryReader.setNotifyListeners(false);
     }
-    entryReaderQueue.removeAll(toRemove);
+  }
+
+  /**
+   * Starts the provider reader.
+   * @param reader the LDAPEntryReader.
+   */
+  private void startReader(LDAPEntryReader reader)
+  {
+    entryReader = reader;
+    if ((entryReaderThread == null) || !entryReaderThread.isAlive())
+    {
+      entryReaderThread = new Thread(new Runnable()
+      {
+        LDAPEntryReader reader;
+        CustomSearchResult sr;
+        Throwable t;
+        public void run()
+        {
+          while (true)
+          {
+            try
+            {
+              synchronized (entryReaderThread)
+              {
+                while ((reader = entryReader) == null)
+                {
+                  entryReaderThread.wait();
+                }
+              }
+              sr = null;
+              t = null;
+              try
+              {
+                sr = reader.processBackgroundTask();
+              }
+              catch (Throwable th)
+              {
+                t = th;
+              }
+              SwingUtilities.invokeAndWait(new Runnable()
+              {
+                public void run()
+                {
+                  reader.backgroundTaskCompleted(sr, t);
+                  if (reader == entryReader)
+                  {
+                    entryReader = null;
+                  }
+                }
+              });
+            }
+            catch (Throwable t)
+            {
+              entryReader = null;
+            }
+          }
+        }
+      });
+      entryReaderThread.start();
+    }
+    synchronized (entryReaderThread)
+    {
+      entryReaderThread.notify();
+    }
   }
 
   /**
diff --git a/opendj-sdk/opends/src/guitools/org/opends/guitools/controlpanel/util/LDAPEntryReader.java b/opendj-sdk/opends/src/guitools/org/opends/guitools/controlpanel/util/LDAPEntryReader.java
index 6e8ffc8..db80d86 100644
--- a/opendj-sdk/opends/src/guitools/org/opends/guitools/controlpanel/util/LDAPEntryReader.java
+++ b/opendj-sdk/opends/src/guitools/org/opends/guitools/controlpanel/util/LDAPEntryReader.java
@@ -52,6 +52,7 @@
   private InitialLdapContext ctx;
   private Set<EntryReadListener> listeners = new HashSet<EntryReadListener>();
   private boolean isOver;
+  private boolean notifyListeners;
 
   /**
    * Constructor of the entry reader.
@@ -62,6 +63,7 @@
   {
     this.dn = dn;
     this.ctx = ctx;
+    this.notifyListeners = true;
   }
 
   /**
@@ -102,7 +104,7 @@
   public void backgroundTaskCompleted(CustomSearchResult sr,
       Throwable throwable)
   {
-    if (!isInterrupted())
+    if (!isInterrupted() && isNotifyListeners())
     {
       if (throwable == null)
       {
@@ -117,6 +119,28 @@
   }
 
   /**
+   * Returns whether this entry reader will notify the listeners once it is
+   * over.
+   * @return whether this entry reader will notify the listeners once it is
+   * over.
+   */
+  public boolean isNotifyListeners()
+  {
+    return notifyListeners;
+  }
+
+  /**
+   * Sets whether this entry reader will notify the listeners once it is
+   * over.
+   * @param notifyListeners whether this entry reader will notify the listeners
+   * once it is over.
+   */
+  public void setNotifyListeners(boolean notifyListeners)
+  {
+    this.notifyListeners = notifyListeners;
+  }
+
+  /**
    * Returns <CODE>true</CODE> if the read process is over and
    * <CODE>false</CODE> otherwise.
    * @return <CODE>true</CODE> if the read process is over and

--
Gitblit v1.10.0