/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at * trunk/opends/resource/legal-notices/OpenDS.LICENSE * or https://OpenDS.dev.java.net/OpenDS.LICENSE. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, * add the following below this CDDL HEADER, with the fields enclosed * by brackets "[]" replaced with your own identifying information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * * Copyright 2006-2009 Sun Microsystems, Inc. * Portions copyright 2012 ForgeRock AS. */ package org.opends.server.core; import org.opends.server.api.DirectoryThread; import java.util.Iterator; import java.util.LinkedList; /** * This class defines a daemon thread that will be used to monitor the server * shutdown process and may help nudge it along if it appears to get hung. */ public class ServerShutdownMonitor extends DirectoryThread { // Indicates whether the monitor has completed and the shutdown may be // finalized with a call to System.exit; private volatile boolean monitorDone; // The list of threads that need to be monitored. private LinkedList<Thread> threadList; /** * Creates a new instance of this shutdown monitor thread that will collect * information about the threads that need to be watched to ensure that they * shut down properly. */ public ServerShutdownMonitor() { super("Directory Server Shutdown Monitor"); setDaemon(true); // Get the thread ID of the current thread, since it is the one that is // actually processing the server shutdown and therefore shouldn't be // interrupted or impeded. long currentThreadID = Thread.currentThread().getId(); // Get the Directory Server thread group and identify all of the non-daemon // threads that are currently active. This can be an inexact science, so // we'll make sure to allocate enough room for double the threads that we // think are currently running. threadList = new LinkedList<Thread>(); ThreadGroup threadGroup = DirectoryThread.DIRECTORY_THREAD_GROUP; Thread[] threadArray = new Thread[threadGroup.activeCount() * 2]; int numThreads = threadGroup.enumerate(threadArray, true); for (int i=0; i < numThreads; i++) { Thread t = threadArray[i]; if (t.isAlive() && (! t.isDaemon()) && (t.getId() != currentThreadID)) { threadList.add(t); } } monitorDone = true; } /** * Operates in a loop, waiting for all threads to be stopped. At certain * milestones, if there are threads still running then it will attempt to * get them to stop. */ public void run() { monitorDone = false; try { // First, check to see if we need to do anything at all. If all threads // are stopped, then we don't have a problem. Iterator<Thread> iterator = threadList.iterator(); while (iterator.hasNext()) { Thread t = iterator.next(); if (! t.isAlive()) { iterator.remove(); } } if (threadList.isEmpty()) { return; } // For the first milestone, we'll run for up to 30 seconds just checking // to see whether all threads have stopped yet. long stopTime = System.currentTimeMillis() + 30000; while (System.currentTimeMillis() < stopTime) { iterator = threadList.iterator(); while (iterator.hasNext()) { Thread t = iterator.next(); if (! t.isAlive()) { iterator.remove(); } } if (threadList.isEmpty()) { return; } else { try { Thread.sleep(10); } catch (Exception e) {} } } // Now we're at the second milestone, where we'll interrupt all threads // that are still running and then wait for up to 30 more seconds for them // to stop. iterator = threadList.iterator(); while (iterator.hasNext()) { Thread t = iterator.next(); try { if (t.isAlive()) { t.interrupt(); } } catch (Exception e) {} } if (threadList.isEmpty()) { return; } stopTime = System.currentTimeMillis() + 30000; while (System.currentTimeMillis() < stopTime) { iterator = threadList.iterator(); while (iterator.hasNext()) { Thread t = iterator.next(); if (! t.isAlive()) { iterator.remove(); } } if (threadList.isEmpty()) { return; } else { try { Thread.sleep(10); } catch (Exception e) {} } } // At this time, we could try to stop or destroy any remaining threads, // but we won't do that because we'll use a System.exit in the thread that // initiated a shutdown and it should take care of anything else that // might still be running. Nevertheless, we'll print an error message to // standard error so that an administrator might see something that needs // to be investigated further. System.err.println("WARNING: The following threads were still active " + "after waiting up to 60 seconds for them to stop:"); iterator = threadList.iterator(); while (iterator.hasNext()) { Thread t = iterator.next(); System.err.println("Thread Name: " + t.getName()); System.err.println("Stack Trace:"); for (StackTraceElement e : t.getStackTrace()) { System.err.print(" " + e.getClassName() + "." + e.getMethodName() + "(" + e.getFileName() + ":"); if (e.isNativeMethod()) { System.err.print("native method"); } else { System.err.print(e.getLineNumber()); } System.err.println(")"); System.err.println(); } } } finally { monitorDone = true; } } /** * Waits for the monitor thread to complete any necessary processing. This * method will not return until the monitor thread has stopped running. */ public void waitForMonitor() { while (! monitorDone) { try { Thread.sleep(10); } catch (Exception e) {} } } }