uk.co.gidley.jmxmonitor.services.ThreadManager.java Source code

Java tutorial

Introduction

Here is the source code for uk.co.gidley.jmxmonitor.services.ThreadManager.java

Source

/*
 * Copyright 2009 Ben Gidley
 *
 *    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.
 */

package uk.co.gidley.jmxmonitor.services;

import org.apache.commons.configuration.Configuration;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import uk.co.gidley.jmxmonitor.monitoring.MonitoringGroup;

import java.io.File;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.ClosedByInterruptException;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;

/**
 * The manager service is responsible for managing the main thread loop and controlling the monitors
 */
public class ThreadManager {
    private static final Logger logger = LoggerFactory.getLogger(ThreadManager.class);
    public static final String SHUTDOWN_MONITOR_THREAD = "ShutdownMonitor";
    private Boolean threadManagerRunning = true;
    private Map<String, MonitoringGroupHolder> monitoringGroups = new HashMap<String, MonitoringGroupHolder>();
    private ThreadGroup threadGroup = new ThreadGroup("MonitoringGroups");
    public static final String PROPERTY_PREFIX = "jmxmonitor.";
    private MainConfiguration mainConfiguration;
    public static final String JMXMONITOR_STOPPORT = "jmxmonitor.stopport";
    public static final String JMXMONITOR_STOPKEY = "jmxmonitor.stopkey";

    public ThreadManager(MainConfiguration mainConfiguration) {
        this.mainConfiguration = mainConfiguration;
    }

    /**
     * Stop this asap (NOT sync)
     */
    public void stop() {
        this.threadManagerRunning = false;
    }

    public void initialise() throws InitialisationException {
        // Start shutdown socket service
        ShutdownRunner shutdownRunner = null;
        try {
            shutdownRunner = new ShutdownRunner(mainConfiguration.getConfiguration());
        } catch (IOException e) {
            logger.error("{}", e);
            throw new InitialisationException(e);
        }
        Thread shutdownThread = new Thread(shutdownRunner, SHUTDOWN_MONITOR_THREAD);

        try {

            shutdownThread.start();

            // Configure Monitoring Group instances
            List<String> monitoringGroupNames = mainConfiguration.getConfiguration()
                    .getList(PROPERTY_PREFIX + "groups");

            for (String groupName : monitoringGroupNames) {
                if (!StringUtils.isEmpty(groupName)) {
                    logger.debug("Started initialising {}", groupName);
                    initialiseMonitoringGroup(groupName, mainConfiguration.getConfiguration());
                    logger.debug("Completed initialising {}", groupName);
                }
            }

            // Start threads to begin monitoring
            logger.info("Configuration complete starting all monitors");
            for (String groupName : monitoringGroups.keySet()) {
                Thread thread = monitoringGroups.get(groupName).getThread();
                thread.start();
            }

            // Continue to monitor for failures or stop message. On failure stop group, restart it if possible
            while (threadManagerRunning) {
                for (String groupName : monitoringGroups.keySet()) {
                    MonitoringGroup monitoringGroup = monitoringGroups.get(groupName).getMonitoringGroup();
                    if (!monitoringGroup.isAlive()) {
                        restartMonitoringGroup(groupName, mainConfiguration.getConfiguration());
                    }
                }

                // Stop if the shutdown thread has triggered. 
                if (!shutdownThread.isAlive()) {
                    threadManagerRunning = false;
                    for (String groupName : monitoringGroups.keySet()) {
                        logger.debug("Stopping {}", groupName);
                        MonitoringGroup monitoringGroup = monitoringGroups.get(groupName).getMonitoringGroup();
                        monitoringGroup.stop();
                    }
                    for (String groupName : monitoringGroups.keySet()) {
                        Thread monitoringGroup = monitoringGroups.get(groupName).getThread();
                        monitoringGroup.join();
                        logger.debug("Stopped {}", groupName);
                    }
                }

                Thread.sleep(5000);
            }

        } catch (NoSuchElementException e) {
            logger.error("{}", e);
            throw new InitialisationException(e);
        } catch (InterruptedException e) {
            logger.error("{}", e);
            throw new RuntimeException(e);
        } finally {
            // Must shutdown the shutdown thread
            shutdownThread.interrupt();

        }

    }

    private void restartMonitoringGroup(String groupName, Configuration config)
            throws InterruptedException, InitialisationException {
        logger.warn("Monitoring Group is dead: {}. Restarting", groupName);

        // Tidy up
        Thread oldThread = monitoringGroups.get(groupName).getThread();
        if (oldThread.isAlive()) {
            // Problem try to interrupt. This should force an exist
            oldThread.interrupt();
            oldThread.join(5000);
            if (oldThread.isAlive()) {
                logger.error("Unable to stop monitor thread {}", groupName);
                throw new RuntimeException("Unable to stop monitor thread " + groupName);
            }
        }
        monitoringGroups.remove(groupName);

        // Restart
        initialiseMonitoringGroup(groupName, config);
        Thread restartThread = monitoringGroups.get(groupName).getThread();
        restartThread.start();
    }

    private void initialiseMonitoringGroup(String groupName, Configuration config) throws InitialisationException {
        String monitorFile = config.getString(PROPERTY_PREFIX + groupName + ".monitorConfiguration");
        String expressionFile = config.getString(PROPERTY_PREFIX + groupName + ".expressionConfiguration");
        Long interval = config.getLong(PROPERTY_PREFIX + groupName + ".interval");

        File monitor = findConfigurationFile(monitorFile);
        File expression = findConfigurationFile(expressionFile);

        MonitoringGroup monitoringGroup = new MonitoringGroup();
        monitoringGroup.initialise(groupName, monitor, expression, interval);
        Thread thread = new Thread(threadGroup, monitoringGroup, groupName);
        monitoringGroups.put(groupName, new MonitoringGroupHolder(monitoringGroup, thread));
    }

    private File findConfigurationFile(String configurationFile) throws InitialisationException {
        File configuration = new File(configurationFile);
        if (!configuration.exists()) {
            throw new InitialisationException(
                    "Referenced Configuration File not found:" + configuration.getAbsolutePath());
        }
        return configuration;
    }

    private class ShutdownRunner implements Runnable {
        private ServerSocketChannel serverSocketChannel;
        private ByteBuffer dbuf = ByteBuffer.allocateDirect(1024);
        private Charset charset = Charset.forName("US-ASCII");
        private CharsetDecoder decoder = charset.newDecoder();
        private String stopKey;

        ShutdownRunner(Configuration config) throws IOException {
            int stopPort = config.getInt(JMXMONITOR_STOPPORT);
            stopKey = config.getString(JMXMONITOR_STOPKEY);
            logger.debug("Initialising ShutdownRunner in {}", stopPort);
            serverSocketChannel = ServerSocketChannel.open();
            serverSocketChannel.socket().bind(new InetSocketAddress("127.0.0.1", stopPort));
            logger.debug("Initialised ShutdownRunner in {}", stopPort);
        }

        @Override
        public void run() {
            logger.info("Stop listener thread working");

            try {
                boolean shutdownRunning = true;
                while (shutdownRunning) {
                    SocketChannel socketChannel = serverSocketChannel.accept();
                    dbuf.clear();
                    socketChannel.read(dbuf);
                    dbuf.flip();
                    CharBuffer cb = decoder.decode(dbuf);
                    if (cb.toString().equals(stopKey)) {
                        serverSocketChannel.close();
                        shutdownRunning = false;
                        logger.info("Recieved Stop command");
                    }
                }
            } catch (ClosedByInterruptException e) {
                // Ok just a shutdown of the main process
                logger.debug("Closing shutdown thread {}", e);
            } catch (IOException e) {
                logger.error("{}", e);
                throw new RuntimeException(e);
            }

        }
    }

    /**
     * Used to hold the monitoring group
     */
    private class MonitoringGroupHolder {
        private MonitoringGroup monitoringGroup;
        private Thread thread;

        private MonitoringGroupHolder(MonitoringGroup monitoringGroup, Thread thread) {
            this.monitoringGroup = monitoringGroup;
            this.thread = thread;
        }

        public MonitoringGroup getMonitoringGroup() {
            return monitoringGroup;
        }

        public void setMonitoringGroup(MonitoringGroup monitoringGroup) {
            this.monitoringGroup = monitoringGroup;
        }

        public Thread getThread() {
            return thread;
        }

        public void setThread(Thread thread) {
            this.thread = thread;
        }
    }

}