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 configured as {@link Runtime#addShutdownHook(Thread)}.
 *
 * @author roland
 * @since Mar 3, 2010
 */
class CleanupThread extends Thread {

    private final HttpServer server;
    private final ThreadGroup threadGroup;
    private boolean active = true;

    /**
     * Constructor associating the cleanup 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() {
        server.stop(0);
    }

    /**
     * Stop the server.
     */
    public void stopServer() {
        active = false;
        server.stop(0);
    }

    // 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 (final Thread t : pThreads) {
            if (isDaemonLikeThread(t)) {
                continue;
            }
            try {
                t.join();
            } catch (Exception ex) {
                // Ignore that one.
            }
            // 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 static final String[] DAEMON_THREAD_NAMES = new String[] {
            // Tanuki Java Service Wrapper (#116)
            "WrapperListener_stop_runner",
            // Shutdown thread
            "DestroyJavaVM"
    }; // Check for threads which should not prevent the server from stopping.
    private boolean isDaemonLikeThread(Thread pThread) {
        // Daemon or part of our thread group
        if (pThread.isDaemon() ||
            pThread.getThreadGroup() == null || // has died on us
            pThread.getThreadGroup().equals(threadGroup)) {
            return true;
        }
        for (String name : DAEMON_THREAD_NAMES) {
           if (pThread.getName().startsWith(name)) {
               return true;
           }
        }
        return false;
    }
}

