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