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