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.core;
028    
029    
030    
031    import java.util.Iterator;
032    import java.util.LinkedList;
033    
034    
035    
036    /**
037     * This class defines a daemon thread that will be used to monitor the server
038     * shutdown process and may help nudge it along if it appears to get hung.
039     */
040    public class ServerShutdownMonitor
041           extends Thread
042    {
043      // Indicates whether the monitor has completed and the shutdown may be
044      // finalized with a call to System.exit;
045      private boolean monitorDone;
046    
047      // The list of threads that need to be monitored.
048      private LinkedList<Thread> threadList;
049    
050    
051    
052      /**
053       * Creates a new instance of this shutdown monitor thread that will collect
054       * information about the threads that need to be watched to ensure that they
055       * shut down properly.
056       */
057      public ServerShutdownMonitor()
058      {
059        setName("Directory Server Shutdown Monitor");
060        setDaemon(true);
061    
062    
063        // Get the thread ID of the current thread, since it is the one that is
064        // actually processing the server shutdown and therefore shouldn't be
065        // interrupted or impeded.
066        long currentThreadID = Thread.currentThread().getId();
067    
068    
069        // Get the Directory Server thread group and identify all of the non-daemon
070        // threads that are currently active.  This can be an inexact science, so
071        // we'll make sure to allocate enough room for double the threads that we
072        // think are currently running.
073        threadList = new LinkedList<Thread>();
074        ThreadGroup threadGroup = DirectoryServer.getDirectoryThreadGroup();
075        Thread[] threadArray = new Thread[threadGroup.activeCount() * 2];
076        int numThreads = threadGroup.enumerate(threadArray, true);
077        for (int i=0; i < numThreads; i++)
078        {
079          Thread t = threadArray[i];
080    
081          if (t.isAlive() && (! t.isDaemon()) && (t.getId() != currentThreadID))
082          {
083            threadList.add(t);
084          }
085        }
086    
087        monitorDone = true;
088      }
089    
090    
091    
092      /**
093       * Operates in a loop, waiting for all threads to be stopped.  At certain
094       * milestones, if there are threads still running then it will attempt to
095       * get them to stop.
096       */
097      public void run()
098      {
099        monitorDone = false;
100    
101        try
102        {
103          // First, check to see if we need to do anything at all.  If all threads
104          // are stopped, then we don't have a problem.
105          Iterator<Thread> iterator = threadList.iterator();
106          while (iterator.hasNext())
107          {
108            Thread t = iterator.next();
109            if (! t.isAlive())
110            {
111              iterator.remove();
112            }
113          }
114    
115          if (threadList.isEmpty())
116          {
117            return;
118          }
119    
120    
121          // For the first milestone, we'll run for up to 30 seconds just checking
122          // to see whether all threads have stopped yet.
123          long stopTime = System.currentTimeMillis() + 30000;
124          while (System.currentTimeMillis() < stopTime)
125          {
126            iterator = threadList.iterator();
127            while (iterator.hasNext())
128            {
129              Thread t = iterator.next();
130              if (! t.isAlive())
131              {
132                iterator.remove();
133              }
134            }
135    
136            if (threadList.isEmpty())
137            {
138              return;
139            }
140            else
141            {
142              try
143              {
144                Thread.sleep(10);
145              } catch (Exception e) {}
146            }
147          }
148    
149    
150          // Now we're at the second milestone, where we'll interrupt all threads
151          // that are still running and then wait for up to 30 more seconds for them
152          // to stop.
153          iterator = threadList.iterator();
154          while (iterator.hasNext())
155          {
156            Thread t = iterator.next();
157            try
158            {
159              if (t.isAlive())
160              {
161                t.interrupt();
162              }
163            } catch (Exception e) {}
164          }
165    
166          if (threadList.isEmpty())
167          {
168            return;
169          }
170    
171          stopTime = System.currentTimeMillis() + 30000;
172          while (System.currentTimeMillis() < stopTime)
173          {
174            iterator = threadList.iterator();
175            while (iterator.hasNext())
176            {
177              Thread t = iterator.next();
178              if (! t.isAlive())
179              {
180                iterator.remove();
181              }
182            }
183    
184            if (threadList.isEmpty())
185            {
186              return;
187            }
188            else
189            {
190              try
191              {
192                Thread.sleep(10);
193              } catch (Exception e) {}
194            }
195          }
196    
197    
198          // At this time, we could try to stop or destroy any remaining threads,
199          // but we won't do that because we'll use a System.exit in the thread that
200          // initiated a shutdown and it should take care of anything else that
201          // might still be running.  Nevertheless, we'll print an error message to
202          // standard error so that an administrator might see something that needs
203          // to be investigated further.
204          System.err.println("WARNING:  The following threads were still active " +
205                             "after waiting up to 60 seconds for them to stop:");
206    
207          iterator = threadList.iterator();
208          while (iterator.hasNext())
209          {
210            Thread t = iterator.next();
211            System.err.println("Thread Name:  " + t.getName());
212            System.err.println("Stack Trace:");
213    
214            for (StackTraceElement e : t.getStackTrace())
215            {
216              System.err.print("              " + e.getClassName() + "." +
217                               e.getMethodName() + "(" + e.getFileName() + ":");
218    
219              if (e.isNativeMethod())
220              {
221                System.err.print("native method");
222              }
223              else
224              {
225                System.err.print(e.getLineNumber());
226              }
227    
228              System.err.println(")");
229              System.err.println();
230            }
231          }
232        }
233        finally
234        {
235          monitorDone = true;
236        }
237      }
238    
239    
240    
241      /**
242       * Waits for the monitor thread to complete any necessary processing.  This
243       * method will not return until the monitor thread has stopped running.
244       */
245      public void waitForMonitor()
246      {
247        while (! monitorDone)
248        {
249          try
250          {
251            Thread.sleep(10);
252          } catch (Exception e) {}
253        }
254      }
255    }
256