Embedded or Stand-alone

Listing 4. The ApplicationMonitorClient object should be consumed by applications written in Java. Applications written in other languages should use its stand-alone functionality.

package Client;

import Server.*;

import java.net.*;
import java.io.*;
import java.util.*;

import Util.*;

import javax.xml.parsers.
   ParserConfigurationException;

import org.xml.sax.SAXException;

/**
* The ApplicationMonitorClient is used to 
* communicate with the ApplicationMonitorServer.  
* It performs the task of registration as well 
* as sending messages and receiving commands.
*/
public class ApplicationMonitorClient extends 
   Thread implements ApplicationMonitorInterface
{
   private DataOutputStream dataOutput; 
   // The output stream of the socket connected to 
   // the ApplicationMonitorServer
   private DataInputStream dataInput; 
   // The input stream of the socket connected to 
   // the ApplicationMonitorServer
   private Socket socket; 
   // The TCP socket connected to the 
   // ApplicationMonitorServer
   private String appName; 
   // The name of the application being registered
   private String args[]; 
   // The command-line arguments used to launch 
   // the client application
   private Vector msgBuffer = new Vector(); 
   // Buffer used to store messages to be sent to 
   // the ApplicationMonitorServer
   private SendMsgThread sendMsgThread; 
   // The thread to send messages to the 
   // ApplicationMonitorServer
   private int restartCount; 
   // Number of automatic restarts assigned to the 
   // client
   private Process currentProcess = null; 
   // The process spawned
   private boolean external = false; 
   // TRUE ApplicationMonitorClient runs an 
   // external process, FALSE it embedded within a 
   // process

   // Message serverity constants
   public final static String WARNING = "WARNING";
   public final static String ERROR = "ERROR";
   public final static String INFO = "INFO";

   // Default version constant
   public final static String VERSION = "NONE";

   // Default: multiple instances are allowed on 
   // any Host on the network.
   private int numberOfInstances = ANYNUMBER;

   // Default: the version is set to NONE
   private String version = VERSION;

   /**
   * Constructor.  Used to launch a given process 
   * and register it with the 
   * ApplicationMonitorServer. The output stream 
   * of the spawned process is captured and sent 
   * as INFO messages to the 
   * ApplicationMonitorServer
   */
   public ApplicationMonitorClient(
      String _appName, int _numberOfInstances, 
      String _version, int _autoRestartCount, 
      String _args[], String _pingArgs[], 
      boolean _external) throws IOException, 
      InterruptedException
   {
      this(_appName, _pingArgs, _numberOfInstances, 
         _version, _autoRestartCount, _external);

      // Spawn the requested process
      Runtime appSpawn = Runtime.getRuntime();
      currentProcess = appSpawn.exec(_args);

      // Capture the spawned process's standard 
      // output and error streams
      InputStream in = 
         currentProcess.getInputStream();
      InputStream err = 
         currentProcess.getErrorStream();
      ProcessExternalStream outputStream = 
         new ProcessExternalStream(in, this);
      ProcessExternalStream errorStream = 
         new ProcessExternalStream(err, this);
      outputStream.start();
      errorStream.start();

      // Wait for the spawn process to complete.  
      // This prevents the ApplicationMonitorClient 
      // for exiting.
      currentProcess.waitFor();
   }

   /**
   * Constructs an ApplicationMonitorClient 
   * instance.  Application specific sets are 
   * stored, a thread to send messages to the 
   * ApplicationMonitorServer is created and 
   * started, and a thread to register and receive 
   * ApplicationMonitorServer commands is started.
   */
   public ApplicationMonitorClient(
      String _appName, String _args[], 
      int _numberOfInstances, String _version, 
      int _restartCount, boolean _external)
   {
      appName = _appName;
      args = _args;
      numberOfInstances = _numberOfInstances;
      version = _version;
      restartCount = _restartCount;
      external = _external;

      // Create and start the thread that sends 
      // messages to the ApplicationMonitorServer
      sendMsgThread = new SendMsgThread(msgBuffer);
      sendMsgThread.start();

      // Start the thread that registers and 
      // receives ApplicationMonitorServer commands
      start();
   }

   /**
   * Thread that registers and receives 
   * ApplicationMonitorServer commands
   */
   public void run()
   {
      createConnection();
   }

   /**
   * Connect to the ApplicationMonitorServer given 
   * the System property defined host and port 
   * 3500.  Once a successful connection is 
   * established the client is registered, and 
   * starts listening for ApplicationMonitorServer
   * commands.
   */
   public void createConnection()
   {
      String host = null;

      while(true)
      {
         try
         {
            File applicationMonitorPropertiesFile = 
               new File(System.getProperty(
               "ApplicationMonitor.PropertyFile", 
               "c:\\temp\\ApplicationMonitorProperties.ini"));
            Properties applicationMonitorProperties = 
               new Properties();
            applicationMonitorProperties.setProperty(
               "ApplicationMonitor.
               ApplicationMonitorServerHost", 
               "localhost");
            if (
               applicationMonitorPropertiesFile.
               exists())
            {
                 FileInputStream fileInput = null;
                  try
                  {
                     fileInput = new FileInputStream(
                        applicationMonitorPropertiesFile);
               applicationMonitorProperties.load(
                  fileInput);
                  }
                  catch(IOException ex)
                  {
                  }
                  finally
                  {
                     if (fileInput != null)
                        try { fileInput.close(); } 
                           catch(Exception ex) { }
                  }
            }
           host = 
            applicationMonitorProperties.getProperty(
               "ApplicationMonitor.
               ApplicationMonitorServerHost");
            socket = new Socket(host, 3500);
            dataOutput = new DataOutputStream(
               socket.getOutputStream());
            dataInput = new DataInputStream(
               socket.getInputStream());
            break;
         }
         catch(IOException ex)
         {
            System.out.println(
               "Error Connecting to Adminstrator: " + 
               ex);
            dataOutput = null;
            dataInput = null;
         }

         try { Thread.sleep(1000); } catch(
            Exception ex) { }
      }

      // Register with the ApplicationMonitorServer
      register();
      sendMsgThread.setDataOutput(dataOutput);

      // Wait for ApplicationMonitorServer commands
      readCommands();
   }

   /**
   * Register the remote application with the 
   * AppliationMonitorServer
   */
   public void register()
   {
      try
      {
         // Create a <Register> XML
         XMLMessageUtil registerMessage = 
            new XMLMessageUtil("Register");
         registerMessage.add("AppName", appName);
         registerMessage.add("Host", 
            InetAddress.getLocalHost().
            getHostAddress());
         registerMessage.add("Version", version);
         registerMessage.add("InstancesAllowed", 
            String.valueOf(numberOfInstances));
         registerMessage.add("RestartCount", 
            String.valueOf(restartCount));

         // If external is TRUE then the 
         // ApplicationMonitorClient is serving as a 
         // wrapper otherwise the java executable 
         // and CLASSPATH are included within the 
         // parameter XML specification.
         if (external)
            registerMessage.add("ParameterNumber", 
               String.valueOf(args.length+3));
         else
            registerMessage.add("ParameterNumber", 
               String.valueOf(args.length+4));

         String javaHome = System.getProperty(
            "java.home");
         if (javaHome != 
            null && javaHome.length() > 0)
         {
            String fileSeparator = 
               System.getProperty("file.separator");
               registerMessage.add("ARG0", javaHome + 

                  fileSeparator + "bin" + fileSeparator + 
                  "java");
         }
         else
            registerMessage.add("ARG0", "java");
         int argCount = 1;
         if (System.getProperty("java.class.path") != 
            null && System.getProperty(
            "java.class.path").length() > 0)
         {
            registerMessage.add("ARG1", "-classpath");
            registerMessage.add("ARG2", 
               System.getProperty("java.class.path"));
            argCount = 3;
         }

         int argStart = 0;
         if (external)
         {
            registerMessage.add("ARG" + argCount, 
               args[0]);
            argStart = 1;
         }
         else
               registerMessage.add("ARG" + argCount, 
                  appName);

         for (int i=argStart; i<args.length; i++)
         {
            argCount++;
            registerMessage.add("ARG" + argCount, 
               args[i]);
         }

         registerMessage.send(dataOutput);
         registerMessage = null;
      }
      catch(IOException ex)
      {
         System.out.println("Error Registering: " + 
            ex);
         try { socket.close(); } catch(
            Exception ex2) { }
         createConnection();
      }
      catch(ParserConfigurationException ex)
      {
         System.out.println(
            "Error Creating Registration Message: " + 
            ex);
      }
   }

   /**
   * Adds a message and serverity to the message 
   * buffer for later processing by the 
   * SendMsgThread object
   */
   public void sendMessage(String _severity, 
      String _msg)
   {
      synchronized(msgBuffer)
      {
         msgBuffer.add(new MsgOb(_severity, _msg));
      }
   }

   /**
   * Read XML commands from the 
   * ApplicationMonitorServer.  Currently the only 
   * command processed is the KILL command.
   */
   public void readCommands()
   {
      try
      {
         while(true)
         {
            try
            {
               // Wait forever util a 
               // ApplicationMonitorServer command is 
               // received
               XMLMessageUtil commandMessage = 
                  new XMLMessageUtil(dataInput);
               if (
                  commandMessage.getRootNodeName().
                  equals("Command"))
               {
                  String action = null;
                  try
                  {
                     action = 
                        commandMessage.getValue("Action");
                           if (action == null)
                              continue;
                  }
                  catch(Exception ex)
                  {
                           continue;
                  }

                  if (action.equals("KILL"))
                  {
                        // If the command is "KILL", kill 
                        // the spawned process (if one 
                        // exists) and exit.
                        if (currentProcess != null)
                              currentProcess.destroy();

                           System.exit(0);
                  }
            }
         }
         catch(ParserConfigurationException ex)
         {
            System.out.println(
               "Error Parsing Incoming XML Command: " + 
               ex);
         }
         catch(SAXException ex)
         {
            System.out.println(
               "Error Parsing Incoming XML Command: " + 
               ex);
         }
      }
   }
   catch(IOException ex)
   {
         System.out.println(
            "Adminstrator has disconnected: " +  ex);
   }

    // If we are here then the socket connected 
    // to the ApplicationMonitorServer 
    // disconnected.  The connection attempted to 
    // be re-established.
    try { socket.close(); } catch(
         Exception ex) { }
      createConnection();
   }

   /**
   * Before we exit, terminate spawned processes 
   * if it exits
   */
   protected void finalize() throws Throwable {
      super.finalize();
      if (currentProcess != null)
         currentProcess.destroy();
   }

   /**
   * Main method is used to launch an external 
   * application for the purpose of registering 
   * and controlling via the 
   * ApplicationMonitorServer@param args[0] 
   * The name of the application to be stored in 
   * the ApplicationMonitorServer's registry
   * @param args[1] The version of the application
   * @param args[2] The number of application 
   * instances allowed (ANYNUMBER | ONEPERHOST | 
   * ONEPERNETWORK)
   * @param args[3] The number of auto restarts 
   * allowed (Use 0 for none, > 0 for auto 
   * restarts)
   * @param args[4] ... args[n] The command-line 
   * arguments required to launch the external 
   * application
   */
   public static void main(String args[])
   {
      try
      {
         if (args.length < 5)
         {
            System.out.println(
               "Incorrect Arguments: Usage");
               System.out.println(
                  "args[0] - The name of the application 
                  to be stored in the 
                  ApplicationMonitorServer's registry");
               System.out.println(
                  "args[1] - The version of the 
                  application");
               System.out.println(
                  "args[2] - The number of application 
                  instances allowed (ANYNUMBER | 
                  ONEPERHOST | ONEPERNETWORK)");
               System.out.println(
                  "args[3] - The number of auto restarts 
                  allowed");
               System.out.println(
                  "args[4] ... args[n] The command-line 
                  arguments required to launch the 
                  external application");
               System.exit(0);
            }

            int numberOfInstances = ANYNUMBER;
            if (args[2].equalsIgnoreCase("ANYNUMBER"))
               numberOfInstances = ANYNUMBER;
            else if (args[2].equalsIgnoreCase(
               "ONEPERHOST"))
               numberOfInstances = ONEPERHOST;
            else if (args[2].equalsIgnoreCase(
               "ONEPERNETWORK"))
               numberOfInstances = ONEPERNETWORK;
            else
            {
               System.out.println(
                  "Number Of Instances Unknown: " + 
                  numberOfInstances);
               System.exit(0);
            }

            String clientArgs[] = 
               new String[args.length+1];
            clientArgs[0] = 
               "Client.ApplicationMonitorClient";

            // Store arguments needed to restart this 
            // application from the 
            // ApplicationMonitorServer
            System.arraycopy(args, 0, clientArgs, 1, 
               args.length);

            // Store arguments needed to exec the 
            // external application
            String launchArgs[] = 
               new String[args.length - 4];
            System.arraycopy(args, 4, launchArgs, 0, 
               args.length-4);

            ApplicationMonitorClient client = 
               new ApplicationMonitorClient(args[0], 
               numberOfInstances, args[1], 
               Integer.parseInt(args[3]), launchArgs, 
               clientArgs, true);
      }
      catch(Exception ex)
      {
            System.out.println(
               "Error Starting ApplicationMonitorClient 
               Wrapped Executable: " + ex);
      }
   }
}