org.jolokia.jvmagent.CleanupThread Maven / Gradle / Ivy
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;
}
}