Rev 7033 | Blame | Compare with Previous | Last modification | View Log | RSS feed
package com.erggroup.buildtool.daemon;import com.erggroup.buildtool.daemon.BuildDaemon;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import java.text.SimpleDateFormat;import java.util.Calendar;import java.util.LinkedHashMap;import java.util.Map.Entry;import java.net.ServerSocket;import java.net.SocketTimeoutException;import java.net.Socket;import java.net.SocketException;import java.io.*;//----------------------------------------------------------------------------// CLASS : NagiosThread//// DESCRIPTION : A class to provide Nagios support, with supplemental commands// This is not an interactive command session// It is intended to take one command and execute it// The primary use is to provide nagios status////public class NagiosThread extends Thread {// Server socketprivate ServerSocket srv;// BuildDaemonprivate BuildDaemon mBuildDaemon;// Flag that indicates whether the poller is running or not.private volatile boolean isRunning = true;// Flag that indicates a request for an immediate kill.private volatile boolean mustDie = false;// Indicate the start time of the daemon// Not nanosecond accurate, just an indication of when it was started// Used to indicate when the daemon was startedprivate final long startTime = System.currentTimeMillis();private final String startString = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss").format(Calendar.getInstance().getTime());/**Logger* @attribute*/private static final Logger mLogger = LoggerFactory.getLogger(NagiosThread.class);// Constructor.public NagiosThread(ServerSocket srv, BuildDaemon bd) {this.isRunning = true;this.srv = srv;this.mBuildDaemon = bd;}// Method for terminating the listenerpublic void terminate() {mLogger.error("NagiosThread terminate");this.isRunning = false;try {srv.close();}catch (IOException e) {mLogger.error("NagiosThread terminate Exception on srv.close: " + e.getMessage());}}/*** Sleep and handle exceptions* @param time - Sleep duration in milliseconds*/private void sleep(int time) {try {Thread.sleep(time);} catch (InterruptedException e) {Thread.currentThread().interrupt();}}/*** This method start the thread and performs all the operations.*/@Overridepublic void run() {mLogger.error("NagiosThread Run");setName("Nagios");// Wait for connection from client.// Process one command than then close the socket// This is not an interactive session//while (isRunning) {Socket socket;try {socket = srv.accept();mLogger.info("Nagios Socket Accepted");processSocketRequest(socket);try {socket.close();} catch (IOException e) {mLogger.error("NagiosThread Exception on socket opr: " + e.getMessage());sleep(1000);}}catch (IOException e) {mLogger.error("NagiosThread Exception on srv.accept: " + e.getMessage());sleep(1000);}}//// Request to terminate this thread// If a mustDie request has been received, then wait 1 second and exit// This is a nasty thing to do//if (mustDie) {terminate();sleep(1000);System.exit(5);}}/*** Process one request* @param socket - Socket with data to process*/void processSocketRequest(Socket socket){// Open a reader to receive possible input// Set read Timeout on the socket to guard against misbehaving users// Insert a dummy 'status' command if no input is received in 3 seconds//try {socket.setSoTimeout(3000);} catch (SocketException e) {mLogger.error("NagiosThread Exception on setSoTimeout: " + e.getMessage());};try {BufferedReader rd = new BufferedReader(new InputStreamReader(socket.getInputStream()));processRequest(socket, rd);rd.close();} catch (IOException e) {mLogger.error("NagiosThread Exception on getInputStream: " + e.getMessage());}}/** Process the request* Have a BufferedReader that will contain the socket data* @param socket - socket for writing response* @param rd - BufferedReader for the socket data*/void processRequest(Socket socket, BufferedReader rd) {String request = null;String[] requestTokens;String requestType = null;try{request = rd.readLine();mLogger.warn("Nagios request:" + request);if (request == null) {return;}//// Simple sanity check on request// First 3 chars must by alphabetic//if (!request.matches("^[A-Za-z]{3}.*")) {mLogger.warn("Nagios request not sane");return;}// Handle restful requests as http requests// Expect rest type url http://server:port/request/arg1/arg2//if (request.matches(".*\\s+HTTP/\\d\\.\\d$")){try {requestTokens = request.split("\\s+");requestType = requestTokens[0];requestTokens = requestTokens[1].substring(1).split("/");request = requestTokens[0];}catch (IndexOutOfBoundsException e1) {mLogger.error("NagiosThread cannot parse HTTP header");return;}}}catch ( SocketTimeoutException s){mLogger.warn("Nagios Socket read timeout");request = "status";} catch (IOException e) {mLogger.error("NagiosThread Exception on processRequest: " + e.getMessage());return;}// Process the users request and generate a responseStringBuilder resp = new StringBuilder();generateResponse(request, resp);//// Generate the output// Wrap in an HTTP header if required// Only really valid for 'estatus' as the content type may not be accurate//try{BufferedWriter wr = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));if(requestType != null){wr.write("HTTP/1.0 200 OK\n");wr.write("Content-Type: application/json\n");wr.write("Content-length: "+ resp.length()+"\n");wr.write("\n");}wr.write (resp.toString());// Flush out the input streamwr.flush();wr.close();} catch (IOException e) {mLogger.error("NagiosThread caught Exception writing to port:" + e.getMessage() );}}/*** Generate a response to the Nagios (or user ) request** @param request - Request to process* @param resp - Build response into this StringBuilder*/void generateResponse(String request, StringBuilder resp) {if (request.equals("status")){// Write the status message to the output stream// Currently this is too simple, but it is a startBuildDaemon.NagiosInfo nagInfo = new BuildDaemon.NagiosInfo();mBuildDaemon.checkThreads(nagInfo);int reasonCount = nagInfo.extendedReason.size();if ( reasonCount <= 0) {resp.append("Build Daemon status: OK\n");} else {resp.append("Build Daemon status: NOT HAPPY\n");mLogger.error("NagiosThread: NOT HAPPY");// Extended information - Reasons for being not happyif (reasonCount > 0){resp.append("Reasons:" + "\n");for( int ii = 0; ii < reasonCount ;ii++){resp.append(" " + nagInfo.extendedReason.get(ii) + "\n");}}}resp.append("Version: "+ this.getClass().getPackage().getImplementationVersion() + "\n");resp.append("HostName: "+ BuildDaemon.mHostname + "\n");resp.append("Started: "+ startString + "\n");resp.append("UpTime (S): "+ (System.currentTimeMillis() - startTime) / 1000 + "\n");resp.append("Threads:" + nagInfo.threadCount + "\n");resp.append("Alive:" + nagInfo.threadAliveCount + "\n");resp.append("Masters:" + nagInfo.masterCount + "\n");resp.append("Slaves:" + nagInfo.slaveCount + "\n");if (BuildDaemon.mShutDown) {resp.append("Shutdown Request in Progress\n");}File cwd = new File(".");resp.append( "Usable space: " + cwd.getUsableSpace() + "\n");}else if ( request.equals( "help") ) {resp.append("Build Daemon Command Interface: Supported commands\n");resp.append(" status - Nagios status\n");resp.append(" estatus - Extended status\n");resp.append(" shutdown - shutdown buildtool\n");resp.append(" kill - Immediate exit\n");resp.append(" help - This message\n");}else if ( request.equals( "estatus") ) {LinkedHashMap<String, Object> basicStatus = new LinkedHashMap<String, Object>();BuildDaemon.NagiosInfo nagInfo = new BuildDaemon.NagiosInfo();mBuildDaemon.checkThreads(nagInfo);basicStatus.put("Vesion", this.getClass().getPackage().getImplementationVersion());basicStatus.put("HostName", BuildDaemon.mHostname );basicStatus.put("Start Text", startString );basicStatus.put("Start Time", Long.valueOf(startTime) );basicStatus.put("UpTime ", Long.valueOf(System.currentTimeMillis() - startTime) );basicStatus.put("Threads" , Integer.valueOf(nagInfo.threadCount) );basicStatus.put("Alive" , Integer.valueOf(nagInfo.threadAliveCount));basicStatus.put("Masters", Integer.valueOf(nagInfo.masterCount));basicStatus.put("Slaves", Integer.valueOf(nagInfo.slaveCount));basicStatus.put("UnHappy" , Integer.valueOf(nagInfo.extendedReason.size()) );basicStatus.put("mShutDown", Boolean.valueOf(BuildDaemon.mShutDown));LinkedHashMap<String, Object> dstatus = new LinkedHashMap<String, Object>();mBuildDaemon.extendedStatus(dstatus);LinkedHashMap<String, Object> status = new LinkedHashMap<String, Object>();status.put("Basic", basicStatus);status.put("Daemon", dstatus);String outBody = dumpJson(status, "");resp.append(outBody);}else if ( request.equals( "shutdown") ) {resp.append("Gracefull shutdown\n");mLogger.error("Gracefull shutdown requested");BuildDaemon.mShutDown = true;}else if ( request.equals( "kill") ) {resp.append("Will commit suicide\n");mLogger.error("Immediate program termination");mustDie = true;isRunning = false;}else {String eMgs = "Build Daemon Command Interface: Unknown command:" + request;mLogger.warn( eMgs );resp.append(eMgs);resp.append("\n");}}/**Display a simple-ish hashmap as JSON* Only handles some basic types of data* - Integers, Longs* - Strings* - Boolean* - null* - Hash Map (recursively)** @param estatus Hash Map to display* @param indent Indentation string* @return json encoded representation of the hashmap*/@SuppressWarnings("unchecked")private String dumpJson(LinkedHashMap<String, Object> estatus, String indent){StringBuilder s = new StringBuilder();String prefix = "";s.append(indent).append("{\n");for (Entry<String, Object> entry : estatus.entrySet()) {String key = entry.getKey();Object obj = entry.getValue();String objType = "";s.append(prefix);prefix = ",\n";s.append(indent).append('"').append(key).append('"').append(": ");if ( obj != null) {objType = obj.getClass().getName();}if ( obj == null ) {s.append("null");} else if (objType.endsWith(".Integer") || objType.endsWith(".Long") ) {s.append(obj.toString());} else if (objType.endsWith(".String") ) {s.append('"').append(obj.toString()).append('"');} else if (objType.endsWith(".Boolean") ) {s.append(obj.toString());} else if (objType.endsWith(".LinkedHashMap") ) {s.append(dumpJson((LinkedHashMap<String, Object>) obj, indent + " "));} else {s.append('"').append("Unknown Class:").append(objType).append('"');}}s.append('\n').append(indent).append("}");return s.toString();}}