package org.jolokia.jvmagent; /* * Copyright 2009-2013 Roland Huss * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import com.sun.net.httpserver.HttpServer; /** * Thread for stopping the HttpServer as soon as every non-daemon * thread has exited. This thread was inspired by the ideas from * Daniel Fuchs (although the implementation is different) * (http://blogs.sun.com/jmxetc/entry/more_on_premain_and_jmx) * * @author roland * @since Mar 3, 2010 */ class CleanupThread extends Thread { private HttpServer server; private ThreadGroup threadGroup; private boolean active = true; /** * Constructor associating the clean up thread with an HTTP-Server * * @param pServer HTTP server to observe * @param pThreadGroup thread group needed for proper cleanup */ CleanupThread(HttpServer pServer, ThreadGroup pThreadGroup) { super("Jolokia Agent Cleanup Thread"); server = pServer; threadGroup = pThreadGroup; setDaemon(true); } /** {@inheritDoc} */ @Override public void run() { try { boolean retry = true; while(retry && active) { // Get all threads, wait for 'foreign' (== not our own threads) // and when all finished, finish as well. This is in order to avoid // hanging endless because the HTTP Server thread cant be set into // daemon mode Thread threads[] = enumerateThreads(); retry = joinThreads(threads); } } finally { // All non-daemon threads stopped ==> server can be stopped, too server.stop(0); } } /** * Stop the server. */ public void stopServer() { active = false; interrupt(); } // Enumerate all active threads private Thread[] enumerateThreads() { boolean fits = false; int inc = 50; Thread[] threads = null; int nrThreads = 0; while (!fits) { try { threads = new Thread[Thread.activeCount()+inc]; nrThreads = Thread.enumerate(threads); fits = true; } catch (ArrayIndexOutOfBoundsException exp) { inc += 50; } } // Trim array Thread ret[] = new Thread[nrThreads]; System.arraycopy(threads,0,ret,0,nrThreads); return ret; } // Join threads, return false if only our own threads are left. private boolean joinThreads(Thread pThreads[]) { for (int i=0;i< pThreads.length;i++) { final Thread t = pThreads[i]; if (t.isDaemon() || t.getThreadGroup() == null || // has died on us t.getThreadGroup().equals(threadGroup) || checkExcludedNames(t.getName())) { // These are threads which should not prevent the server from stopping. continue; } try { t.join(); } catch (Exception ex) { // Ignore that one. } finally { // We just joined a 'foreign' thread, so we redo the loop return true; } } // All 'foreign' threads has finished, hence we are prepared to stop return false; } private boolean checkExcludedNames(String pName) { for (String s : new String[] { "WrapperListener_stop_runner", // Tanuki Java Service Wrapper (#116) "DestroyJavaVM" }) { if (pName.startsWith(s)) { return true; } } return false; } }