Package tcg.syscontrol

Source Code of tcg.syscontrol.ManagedProcess$PrimaryPollerThread

/**
* Redundance in stand-alone mode:
*
* When running in stand-alone mode, the proper way to switch is to set the currently
* in control process to monitor and then let the monitor process takes over.
*
* Two special cases could happen:
* - If we switch the monitor process to control without switching the control process,
*   both process will be in control.
* - If we switch the control process when there is no monitor process running, the
*   control process will switch to monitor and there is no way that it would switch
*   back automatically. (i.e. do not expect the process to know that there is no
*   monitor process thus switching back to control)
*/
package tcg.syscontrol;

import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Properties;
import java.util.Vector;

import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;

import tcg.common.CorbaHelper;
import tcg.common.CorbaManager;
import tcg.common.DatabaseManager;
import tcg.common.LoggerManager;
import tcg.common.Utilities;
import tcg.common.DatabaseManager.DatabaseType;
import tcg.common.util.LoggerWriter;
import tcg.common.util.Native;
import tcg.syscontrol.cos.CosTerminationCodeEnum;
import tcg.syscontrol.cos.DEFAULT_MGR_PORT;
import tcg.syscontrol.cos.ICosManagedProcess;
import tcg.syscontrol.cos.ICosManagedProcessHelper;
import tcg.syscontrol.cos.ICosProcessManager;
import tcg.syscontrol.cos.ICosProcessManagerHelper;
import tcg.syscontrol.cos.LOG_FILE_KEY;
import tcg.syscontrol.cos.LOG_LEVEL_KEY;
//import tcg.syscontrol.cos.CENTRAL_DB_PASSWORD_KEY;
//import tcg.syscontrol.cos.CENTRAL_DB_TNSNAME_KEY;
//import tcg.syscontrol.cos.CENTRAL_DB_USERNAME_KEY;
//import tcg.syscontrol.cos.PRIMARY_DB_PASSWORD_KEY;
//import tcg.syscontrol.cos.PRIMARY_DB_TNSNAME_KEY;
//import tcg.syscontrol.cos.PRIMARY_DB_TYPE_KEY;
//import tcg.syscontrol.cos.PRIMARY_DB_USERNAME_KEY;
//import tcg.syscontrol.cos.STANDBY_DB_PASSWORD_KEY;
//import tcg.syscontrol.cos.STANDBY_DB_TNSNAME_KEY;
//import tcg.syscontrol.cos.STANDBY_DB_USERNAME_KEY;
import tcg.syscontrol.cos.CosLogLevelEnum;
import tcg.syscontrol.cos.CosOperationModeEnum;
import tcg.syscontrol.cos.CosPollException;
import tcg.syscontrol.cos.CosPollNotInModeException;
import tcg.syscontrol.cos.CosProcessStatusEnum;
import tcg.syscontrol.cos.CosProcessTypeEnum;
import tcg.syscontrol.cos.CosRunParamStruct;
import tcg.syscontrol.cos.ICosManagedProcessPOA;

public abstract class ManagedProcess extends ICosManagedProcessPOA
{
  private static final String DEF_ENTITYNAME = "ManagedProcess";
  private static final String DEF_CONFIGFILE = "scada.properties";
  private static final int DEF_PEER_SYNCRATE = 1000;    //in msec
 
  //how often we print out the current state for debugging
  protected static final int VERBOSE_STATE_DEBUGGING_COUNTER = 100;
 
  //default error threshold
  @SuppressWarnings("unused")
  private static final short DEF_ERROR_THRESHOLD = 3;

  //how many times I attempt to do failed corba operation
  //NOTE: internally, jacorb has tried several times before reporting failure on a
  //      corba operation. so we do not need to try multiple times anymore
  private static final int CORBA_ERROR_THRESHOLD = 1;
 
  protected Logger logger = null;
  protected LoggerWriter loggerWriter = null;
   
  protected short weightage = 0;
  protected ICosProcessManager processManager = null;
 
  protected CosProcessStatusEnum state = CosProcessStatusEnum.StatUnstarted;
  protected CosProcessStatusEnum requestedState = CosProcessStatusEnum.StatUnstarted;

  protected Properties props = new Properties();    //contain whatever I got from process manager
  //protected Properties configProps = new Properties();    //configuration properties
 
  protected long pid = 0;
  protected String entityName = DEF_ENTITYNAME;
  protected int managerPort = DEFAULT_MGR_PORT.value;
  protected String logFile = "";
  protected String configFile = DEF_CONFIGFILE;
  protected boolean hasManager = true;
 
  protected String peerName = "";      //peer hostname (if running as stand-alone)
  protected int peerPort = 0;        //peer port number (if running as stand-alone)
  protected boolean isPrimary = false//run as primary (if running as stand-alone)
  protected ICosManagedProcess peerProcess = null;
  protected int peerSyncRate = DEF_PEER_SYNCRATE; 
 
  private PrimaryPollerThread peerPollingThread_ = new PrimaryPollerThread();
  private StandbyNotifierThread peerNotifierThread_ = new StandbyNotifierThread();
 
  //working directory
  protected String workingDir = "";
 
  //set this one if you want to have a persistent servant accessable via corbaloc
  protected int corbaPort = 0;
  //protected String corbaName = "";
 
  protected java.lang.Object mutex = new java.lang.Object();
  protected boolean keepRunning = false;
 
  protected boolean isConfigured = false;
 
  //the command line argument options
    protected Options options = new Options();

    //the main thread
    protected Thread thread = null;
   
    //if we want to start this as in-process thread, what we should do
  public abstract void execute(String args);
 
  //stop the in-process thread
  public void stop()
  {
    requestedState = CosProcessStatusEnum.StatStopped;
    //disable running flag
    isConfigured = true;
    keepRunning = false;
    //trigger the main thread. only expect ONE main thread!!!
    if (thread != null)
    {
      thread.interrupt();
    }
  }
 
  /*
   * Abstract Subroutines
   */
 
  /*
   * The child specific configure routines
   */
  //called during initialization
  protected abstract boolean initialize2(String[] args, Properties props);
 
  //called after the process manager has sent runtime params
  protected abstract boolean configureApplication2();
 
  //called when we are going to/leaving control mode
  protected abstract boolean startControl();
  protected abstract boolean stopControl();

  //called when we are going to/leaving monitor mode
  protected abstract boolean startMonitor();
  protected abstract boolean stopMonitor();
 
  //get the status string
  protected abstract String getStatusString();

  //default ctor
  public ManagedProcess()
  {
    //default contructor
  }

  //default dtor
  protected void finalize() throws Throwable
  {
    CorbaManager.shutdown();
    CorbaManager.cleanup();
  }
 
  protected static void printUsage()
  {
    System.out.println("Managed Process Command Line Parameters:  ");
    System.out.println(" -e  | --entity <entity-name>       Entity name (required)");
    System.out.println(" -p  | --pid <process-id>           Process id");
    System.out.println(" -f  | --config-file <config-file>  Configuration file");
    System.out.println(" -m  | --mgr-port <manager-port>    Manager port");
    System.out.println(" -l  | --logfile <log-file-name>    Log file path");
    System.out.println(" -d  | --working-dir <directory>    Working directory");
    System.out.println(" -cp | --corba-port <port-no>       Corba port to bind to");
    System.out.println(" -nm | --nomgr                      Run as stand-alone (no process manager)");
    System.out.println(" -pn | --peer-name <host-name>      Peer hostname (if running as stand-alone)");
    System.out.println(" -pp | --peer-port <port-no>        Peer port number (if running as stand-alone)");
    System.out.println(" -pm | --primary                    Run as primary server (if running as stand-alone)");
    System.out.println(" -h  | --help                       Print out this help");
    System.out.println(" -v  | --version                    Print out program version");
    System.out.println("");
    System.out.println("Managed Process Java Properties (Configuration file or System properties):  ");
    System.out.println(" tcg.db.type         Database type");
    System.out.println(" tcg.db.name         Database TNS name");
    System.out.println(" tcg.db.user         Database username");
    System.out.println(" tcg.db.password     Database password");
    System.out.println(" tcg.db.encyrpted    Whether the password is encrypted");
    System.out.println(" tcg.event.server1   Primary event server");
    System.out.println(" tcg.event.server2   Secondary event server");
  }

  public boolean initialize(String[] args)
  {
    state = CosProcessStatusEnum.StatStartup;

    //parameters passed as environment properties
        pid = __parseInt(System.getProperty("pid", "0"));
        entityName = System.getProperty("entityName", DEF_ENTITYNAME);
        configFile = System.getProperty("propertiesFile", DEF_CONFIGFILE);
   
        //parse the arguments
        if (!parseArguments(args))
        {
      state = CosProcessStatusEnum.StatUnstarted;   
      return false;
        }
       
        //reset the logging in case it is not yet done by child class
        //otherwise, this function will do nothing
        if (logFile.length() > 0)
        {
          LoggerManager.setLogFile(logFile);
        }
       
        //load all configuration
        props = load_properties(configFile, props);
       
        //load the database configuration
        if (!configure_database_connection())
        {
          logger.error("Can not configure database connnection.");
      state = CosProcessStatusEnum.StatUnstarted;   
          return false;
        }
       
        //specific initialization for child class
        if (!initialize2(args, props))
        {
          logger.error("Can not initialize child process.");
      state = CosProcessStatusEnum.StatUnstarted;   
          return false;
        }

        //initialize corba manager. if corbaPort = 0, the port is randomly allocated
        //if it is already initialized by the child class, it does nothing
        if (!CorbaManager.initialize(corbaPort, managerPort))
        {
          logger.error("Can not initialize CORBA manager.");
      state = CosProcessStatusEnum.StatUnstarted;   
          return false;
        }
   
        //activate this servant.
        //if corbaPort != "", the servant is created as persistent object and
        //  can be acessed via corbaloc address: corbaloc::<ip-addr>:<port>/<corba-name>
        if (!CorbaManager.activate(this, entityName))
        {
          logger.error("Can not activate CORBA servant.");
      state = CosProcessStatusEnum.StatUnstarted;   
          return false;
        }

        //make sure we have a valid manager port
        if (hasManager)
        {
          if (managerPort == 0)
          {
            managerPort = DEFAULT_MGR_PORT.value;
          }
 
          //register with process manager
          if (!register_with_process_manager())
          {
            //failed. do not continue.
            logger.error( "Couldn't register with Process Manager!" );
        state = CosProcessStatusEnum.StatUnstarted;   
            return false;
          }

          //wait for runtime params from process manager
        requestedState = CosProcessStatusEnum.StatStarted;
        }
        else
        {
          //make sure the reference to process manager is null, in case somebody is messing with it
          processManager = null;
          //no need to register with process manager. but connect to peer process (if given)
          if (!connect_to_peer_process())
          {
            //failed. but not a fatal error
            logger.warn( "Couldn't connect to peer process!" );
          }
          //no need to wait for runtime parameters from process manager. create default
          //(1)operation mode: if running as primary, always start as control
          //                   if running as standby, only run as control if peer is not available
          if (isPrimary)
          {
            logger.info( "Running as primary. Will go to CONTROL." );
            requestedState = CosProcessStatusEnum.StatRunningControl;
          }
          else if (peerProcess == null)
          {
            logger.info( "Running as standby but no peer. Will go to CONTROL." );
            requestedState = CosProcessStatusEnum.StatRunningControl;
          }
          else
          {
            logger.info( "Running as standby with peer (primary). Will go to MONITOR." );
            requestedState = CosProcessStatusEnum.StatRunningMonitor;
          }
          //(2)default log file to the current directory
          if (logFile.length() == 0)
          {
            logFile = "./" + entityName + ".log";
            LoggerManager.setLogFile(logFile);
          }
        //(3)call the child custom configure application
        if (!configureApplication2())
        {
          logger.error("Can not configure child process.");
          state = CosProcessStatusEnum.StatUnstarted;   
          return false;
        }
        //(4)set the configure flag right away
          isConfigured = true;
        }
       
        //update status
      changeStatus(CosProcessStatusEnum.StatStarted);
     
      return true;
  }

  protected boolean parseArguments(String[] args)
  {
        //command line arguments
        options = new org.apache.commons.cli.Options();
       
    Option arg = new Option("e", "Entity name (required)");
    arg.setRequired(true);
    arg.setLongOpt("entity");
    arg.setArgs(1);
    arg.setArgName("entity-name");   
    options.addOption(arg);
   
    arg = new Option("f", "Configuration file (optional)");
    arg.setRequired(false);
    arg.setLongOpt("config-file");
    arg.setArgs(1);
    arg.setArgName("file-name");   
    options.addOption(arg);
   
    arg = new Option("p", "Process Id (optional)");
    arg.setRequired(false);
    arg.setLongOpt("pid");
    arg.setArgs(1);
    arg.setArgName("process-id");   
    options.addOption(arg);
   
    arg = new Option("m", "Manager port (optional)");
    arg.setRequired(false);
    arg.setLongOpt("mgr-port");
    arg.setArgs(1);
    arg.setArgName("port-no");   
    options.addOption(arg);
   
    arg = new Option("l", "Log file (optional)");
    arg.setRequired(false);
    arg.setLongOpt("logfile");
    arg.setArgs(1);
    arg.setArgName("log-file");   
    options.addOption(arg);
   
    arg = new Option("d", "Working directory (optional)");
    arg.setRequired(false);
    arg.setLongOpt("working-dir");
    arg.setArgs(1);
    arg.setArgName("port-no");   
    options.addOption(arg);
   
    arg = new Option("cp", "Corba port (optional)");
    arg.setRequired(false);
    arg.setLongOpt("corba-port");
    arg.setArgs(1);
    arg.setArgName("port-no");   
    options.addOption(arg);
   
    arg = new Option("nm", "Run as stand-alone (optional)");
    arg.setRequired(false);
    arg.setLongOpt("nomgr");
    arg.setArgs(0);
    options.addOption(arg);
   
    arg = new Option("pn", "Peer hostname (if running as stand-alone)");
    arg.setRequired(false);
    arg.setLongOpt("peer-name");
    arg.setArgs(1);
    arg.setArgName("host-name");   
    options.addOption(arg);
   
    arg = new Option("pp", "Peer port number (if running as stand-alone)");
    arg.setRequired(false);
    arg.setLongOpt("peer-port");
    arg.setArgs(1);
    arg.setArgName("port-no");   
    options.addOption(arg);
       
    arg = new Option("pm", "Run as primary server (if running as stand-alone)");
    arg.setRequired(false);
    arg.setLongOpt("primary");
    arg.setArgs(0);
    options.addOption(arg);
   
    //parser
        org.apache.commons.cli.Parser parser = new org.apache.commons.cli.GnuParser();
       
        //parse command line arguments
        org.apache.commons.cli.CommandLine cmd = null;
        try
        {
          cmd = parser.parse(options, args, true);
        }
        catch(org.apache.commons.cli.ParseException pe)
        {
      logger.error("Can not parse arguments: " + pe.toString());
      HelpFormatter formatter = new HelpFormatter();
      formatter.printHelp( "Command line parameters:", options );
      return false;
        }
       
        //get the value
        //entity name
        if (cmd.hasOption("e"))
        {
          entityName = cmd.getOptionValue("e");
          props.put("tcg.runtime.entity", entityName);
        }
        //pid
        if (cmd.hasOption("p"))
        {
           pid = __parseInt(cmd.getOptionValue("p"));
        }
        else
        {
          pid = Utilities.getPid();
        }
      props.put("tcg.runtime.pid", Long.toString(pid));
       
        //config/properties file
        if (cmd.hasOption("f"))
        {
          configFile = cmd.getOptionValue("f");
          props.put("tcg.runtime.configfile", configFile);
        }
        //manager port
        if (cmd.hasOption("m"))
        {
           managerPort = __parseInt(cmd.getOptionValue("m"));
          props.put("tcg.runtime.managerport", Integer.toString(managerPort));
        }
        //log file
        if (cmd.hasOption("l"))
        {   
          //return null if the parameter is not set
           logFile = cmd.getOptionValue("l");
          props.put("tcg.runtime.logfile", logFile);
          //instance.setLogFile(logfile);
        logger.info("logfile = " + logFile);
        }
        //corba port
        if (cmd.hasOption("cp"))
        {   
           corbaPort = __parseInt(cmd.getOptionValue("cp"));
          props.put("tcg.runtime.corbaport", Integer.toString(corbaPort));
        }
        //run as stand-alone flag
        if (cmd.hasOption("nm"))
        {
          hasManager = false;
          props.put("tcg.runtime.nomanager", "true");
        }
        //peer host name
        if (cmd.hasOption("pn"))
        {
          peerName = cmd.getOptionValue("pn");
          props.put("tcg.runtime.peername", peerName);
        }
        //peer port number
        if (cmd.hasOption("pp"))
        {
          peerPort = __parseInt(cmd.getOptionValue("pp"));
          props.put("tcg.runtime.peerport", Integer.toString(peerPort));
        }
        //run as primary flag
        if (cmd.hasOption("pm"))
        {
          isPrimary = true;
          props.put("tcg.runtime.primary", "true");
        }

        return true;
  }
 
  //Load properties file and filter out unneeded/empty properties
  //It is also try to get the system properties first before overriding it
  // with properties from file, if any.
  private Properties load_properties(String fileName, Properties props)
  {
    Properties retprops = new Properties();
   
    //Get from the system properties first
    Properties pt = System.getProperties();
        for(Enumeration<?> enumeration = pt.propertyNames(); enumeration.hasMoreElements();)
        {
            String key = (String) enumeration.nextElement();
            String value = pt.getProperty(key);
            if ((key.startsWith("tcg."))
                && value!=null && retprops.getProperty(key)==null)
            {
              retprops.setProperty(key, value);
            }
            //special entry. orb
            if (key.startsWith("org.omg.") && value!=null && retprops.getProperty(key)==null)
            {
              retprops.setProperty(key, value);
            }
        }
   
    //open the property file.
    pt = new Properties();
    InputStream stream = ClassLoader.getSystemResourceAsStream(fileName);
    if (stream == null)
    {
      logger.error("Couldn't find " + fileName + " in classpath.");
    }
    else
    {
      //load the properties file
      try
      {
        pt.load(stream);
      }
      catch(IOException ioe)
      {
        logger.error("Fail to load " + fileName + ". Exception: " + ioe.getMessage());
      }
      finally
      {
        //close the stream
        try
        {
          stream.close();
        }
        catch (IOException ioe)
        {
          //ignore
        }
      }
     
      //override the system properties with properties from file
          for(Enumeration<?> enumeration = pt.propertyNames(); enumeration.hasMoreElements();)
          {
              String key = (String) enumeration.nextElement();
              String value = pt.getProperty(key);
              if (value!=null)
              {
                retprops.setProperty(key, value);
              }
          }
    }  //if (stream == null) - else
   
    //add in any runtime params
    if (props != null)
    {
      //override any other properties if any
          for(Enumeration<?> enumeration = props.propertyNames(); enumeration.hasMoreElements();)
          {
              String key = (String) enumeration.nextElement();
              String value = props.getProperty(key);
              if (value!=null)
              {
                retprops.setProperty(key, value);
              }
          }
    }
   
    return retprops;
  }
 
  private boolean register_with_process_manager()
  {
        //build the corbaloc ior
    String corbaloc = "corbaloc::localhost:" + managerPort + "/ProcessManager";
       
        //verbose
        if (logger.isTraceEnabled())
          logger.trace("corbaloc: " + corbaloc);
       
        //reset the reference
        processManager = null;
       
        //get the reference
        try
        {
        org.omg.CORBA.Object obj = CorbaManager.stringToObject(corbaloc);
        processManager = ICosProcessManagerHelper.narrow(obj);
        }
        catch (Exception ex)
        {
          logger.warn("Can not build reference to process manager: " + corbaloc);
          return false;
        }
       
        for (int i=0; i<CORBA_ERROR_THRESHOLD; i++)
        {
            try
            {
            processManager.cosRegisterManagedProcess(entityName, CosProcessTypeEnum.ProcThread,
                                  this._this(), pid)
            //succesful
            return true;
          }
            catch ( Exception ex )
            {
              logger.error( "(" + i + ") Couldn't register with Process Manager: " + corbaloc );
              //ignore
            }  
        }
       
        //reset the reference
        processManager = null;
       
        return false;
  }
 
  private boolean connect_to_peer_process()
  {
    //validation: peer info is not provided
    if (peerName.length() == 0 && peerPort == 0)
    {
      return false;
    }
        //build the corbaloc ior
    String hostname = peerName;
    if (hostname.length() == 0)
    {
      hostname = "localhost";
    }
    int portno = peerPort;
    if (portno == 0)
    {
      portno = corbaPort;
    }
   
    String corbaloc = "corbaloc::" + hostname + ":" + portno + "/" + entityName;       
        //verbose
        if (logger.isTraceEnabled())
          logger.trace("corbaloc: " + corbaloc);
       
        //reset the reference
        peerProcess = null;

      //get the reference
        try
        {
        org.omg.CORBA.Object obj = CorbaManager.stringToObject(corbaloc);
        peerProcess = ICosManagedProcessHelper.narrow(obj);
        }
        catch (Exception ex)
        {
          logger.warn("Can not build reference to peer process: " + corbaloc);
          return false;
        }
       
        for (int i=0; i<CORBA_ERROR_THRESHOLD; i++)
        {
            try
            {
            peerProcess.cosPoll();
            return true;
          }
            catch ( Exception ex )
            {
              logger.error( "(" + i + ") Couldn't connect to peer process: " + corbaloc );
              //ignore
            }  
        }
       
        //reset the reference
        peerProcess = null;
  
        return false;
  }
 
  private boolean configure_database_connection()
  {
    boolean isEncrypted = false;
    if (0 == props.getProperty("tcg.db.encrypted", "").compareToIgnoreCase("true"))
    {
      isEncrypted = true;
    }
   
    //get the database type
    DatabaseType dbType = null;
    String dbTypeString = props.getProperty("tcg.db.type", "");
    if (dbTypeString.equalsIgnoreCase("ORACLE"))
    {
      dbType = DatabaseType.ORACLE;
    }
    else if (dbTypeString.equalsIgnoreCase("MYSQL"))
    {
      dbType = DatabaseType.MYSQL;
    }
    else
    {
      dbType = DatabaseType.HSQLDB;      //default
    }
   
    //configure primary database connection
    String dbTnsname = props.getProperty("tcg.db.name", "");;
    String dbUsername = props.getProperty("tcg.db.user", "");
    String dbPassword = props.getProperty("tcg.db.password", "");
    if (isEncrypted)
    {
      dbPassword = DatabaseManager.decrypt(dbUsername);
    }
    DatabaseManager.configure(dbType, dbTnsname, dbUsername, dbPassword);
   
    return true;
  }
 
  protected boolean configureApplication()
  {
    logger.trace("configureApplication()");
   
    //ignore database connection parameter from process manager.
    //now, load it from properties file directly
//    boolean isEncrypted = false;
//    if (0 == props.getProperty("tcg.db.encrypted", "").compareToIgnoreCase("true"))
//    {
//      isEncrypted = true;
//    }
//   
//    //configure primary database connection
//    String dbTnsname = props.getProperty(PRIMARY_DB_TNSNAME_KEY.value, "");
//    String dbUsername = props.getProperty(PRIMARY_DB_USERNAME_KEY.value, "");
//    String dbPassword = props.getProperty(PRIMARY_DB_PASSWORD_KEY.value, "");
//    String dbTypeString = props.getProperty(PRIMARY_DB_TYPE_KEY.value, "");
//    //if any of the parameter is not given, try to use properties from config file.
//    if (dbTnsname.length() == 0)
//    {
//      dbTnsname = props.getProperty("tcg.db.name", "");
//    }
//    if (dbUsername.length() == 0)
//    {
//      dbUsername = props.getProperty("tcg.db.user", "");
//    }
//    if (dbPassword.length() == 0)
//    {
//      dbPassword = props.getProperty("tcg.db.password", "");
//      if (isEncrypted)
//      {
//        dbPassword = DatabaseManager.decrypt(dbUsername);
//      }
//    }
//    //get the database type
//    DatabaseType dbType = null;
//    if (dbTypeString.length() == 0)
//    {
//      dbTypeString = props.getProperty("tcg.db.type", "");
//      if (dbTypeString.equalsIgnoreCase("ORACLE"))
//      {
//        dbType = DatabaseType.ORACLE;
//      }
//      else if (dbTypeString.equalsIgnoreCase("MYSQL"))
//      {
//        dbType = DatabaseType.MYSQL;
//      }
//      else
//      {
//        dbType = DatabaseType.HSQLDB;      //default
//      }
//    }
//    DatabaseManager.configure(dbType, dbTnsname, dbUsername, dbPassword);

    //run the custom configuration
    if (!configureApplication2())
    {
      logger.trace("configureApplication() exit: FAILED");
      return false;
    }
   
    isConfigured = true;

    logger.trace("configureApplication() exit: SUCCESSFUL");
    return true;
  }
 
  protected void changeStatus(CosProcessStatusEnum status)
  {
    this.state = status;
    if (hasManager && processManager != null)
    {
      try {
        processManager.cosProcessStatusChanged(entityName, status);
      } catch (Exception ex) {
        logger.warn("Couldn't update status: " + ex.toString());
      }
    }
    else
    {
      //direct peer synchronization
      if (status == CosProcessStatusEnum.StatRunningControl)
      {
        //start the peer notifier thread
        peerNotifierThread_.start();
      }
      else if (status == CosProcessStatusEnum.StatGoingToMonitor
          || status == CosProcessStatusEnum.StatRunningMonitor)
      {
        //start peer poller thread, if it is not yet started
        peerPollingThread_.start();
      }
    }
  }
 
  private boolean can_go_to_monitor()
  {
    boolean status = false;

    if (hasManager && processManager != null)
    {
      try {
        status = processManager.cosProcessGoingToMonitor(entityName);
      } catch (Exception ex) {
        logger.warn("Couldn't get permission to monitor: " + ex.toString());
      }
    }
    else
    {
      //When running as stand-alone, the only time this is called is when we set
      //requested state in initialize() or when there is external CORBA call.
      //Either way we should just let it.
      //NOTE: Another possible implementation is to only let it go to monitor
      //      if and only if the the peer is in CONTROL mode.
      //      This would guarantee that at least one process is in CONTROL.
      //      But this block a legitimate case when running a single instance of
      //      managed process (without peer) and we want to set it to monitor
      //      manually.
      //      It might also trigger back-and-forth switching between a pair of
      //      process. In fact it might prevent swithing altogether because the
      //      standby process can not switch to control unless the primary is in
      //      monitor (or dead) but the primary process can not go to monitor
      //      unless the other process (the standby process) is in control!
      return true;
    }
    return status;
  }
 
  private boolean can_go_to_control()
  {
    boolean status = false;
    //if we do not have manager, always allow
    if (hasManager && processManager != null)
    {
      try {
        status = processManager.cosProcessGoingToControl(entityName);
      } catch (Exception ex) {
        logger.warn("Couldn't get permission to control: " + ex.toString());
      }
    }
    else
    {
      if (isPrimary)
      {
        //we are primary. always allow
        return true;
      }
      else
      {
        //make sure we really try
        if (peerProcess == null)
        {
          connect_to_peer_process();
        }
        if (peerProcess == null)
        {
          //peer is not running or not contactable
          return true;
        }
        else
        {
          //peer is running. make sure peer is in monitor
          try
          {
            peerProcess.cosPollMonitor();
            status = true;
          }
          catch (Exception ex)
          {
            //ignore
          }
        }  //if (peerProcess == null) - else
      //if (isPrimary) - else
    }
    return status;
  }
 
  protected void run()
  {
      //store ior into persistence storage
    CorbaManager.persistIor(entityName, this._this());
     
    logger.info("---- MAIN LOOP ----")//ignore

    //keep reference to the main thread
    thread = Thread.currentThread();
   
      //hopefully this prevent more than one main thread!
    synchronized(mutex)
    {
       keepRunning = true;
      //wait until the application is properly configured
      while (!isConfigured && keepRunning)
      {
        try
        {
          logger.debug("Waiting for configuration...")//ignore
          Thread.sleep(1000);
        }
        catch (InterruptedException ie)
        {
          logger.info("Sleep interrupted.")//ignore
        }
      }
      int counter = 0;
      //main loop
      while (keepRunning)
      {
        if (state == CosProcessStatusEnum.StatTerminating)
        {
          //this is a hack. should never get into here
          keepRunning = false;
          break;
        }
       
        //block unless notified by other thread
        counter = 0;
        while (state == requestedState)
        {
          logger.debug("Process " + entityName + " current state: " +
                CorbaHelper.ProcessStateToString(state));
          counter++;
          if (counter == VERBOSE_STATE_DEBUGGING_COUNTER)
          {
            logger.debug("Process " + entityName + " current state: " +
                CorbaHelper.ProcessStateToString(state));
            counter = 0;
          }
          //TODO -> find out why wait() doesn't work!!!
          //__wait(mutex, keepRunning);
          try
          {
            Thread.sleep(1000);
          }
          catch (InterruptedException ie)
          {
            logger.info("Sleep interrupted.");
            //ignore
          }
          if (!keepRunning) break;
        }
       
        //change state requested
        CosProcessStatusEnum prevstate = CosProcessStatusEnum.StatUnstarted;
        switch(requestedState.value())
        {
        case CosProcessStatusEnum._StatRunningMonitor:
          logger.info("Process " + entityName + " is going to MONITOR");
          if (!can_go_to_monitor())
          {
            logger.warn("Process " + entityName + " is not allowed to go to MONITOR");
            requestedState = state;
            break;
          }
         
          prevstate = state;
          changeStatus(CosProcessStatusEnum.StatGoingToMonitor);
          //if prev state is control. stop it. this is a switch
          if (prevstate == CosProcessStatusEnum.StatRunningControl)
          {
            stopControl();
          }

          //run the monitor loop
          if (!startMonitor())
          {
            //fail. try to go back to previous state
            logger.info("Process " + entityName + " failed to switch to MONITOR. Returning to previous state: "
                  + CorbaHelper.ProcessStateToString(prevstate));
            requestedState = prevstate;
          }
          else
          {
            //notify process manager
            changeStatus(CosProcessStatusEnum.StatRunningMonitor);
          }
          break;
        case CosProcessStatusEnum._StatRunningControl:
          logger.info("Process " + entityName + " is going to CONTROL");
          if (!can_go_to_control())
          {
            logger.warn("Process " + entityName + " is not allowed to go to CONTROL");
            requestedState = state;
            break;
          }

          prevstate = state;
          changeStatus(CosProcessStatusEnum.StatGoingToControl);
          //if prev state is control. stop it. this is a switch
          if (prevstate == CosProcessStatusEnum.StatRunningMonitor)
          {
            stopMonitor();
          }
          //run the control loop
          if (!startControl())
          {
            //fail. try to go back to previous state
            logger.info("Process " + entityName + " failed to switch to CONTROL. Returning to previous state: "
                + CorbaHelper.ProcessStateToString(prevstate));
            requestedState = prevstate;
          }
          else
          {
            //notify process manager
            changeStatus(CosProcessStatusEnum.StatRunningControl);
          }
          break;
        case CosProcessStatusEnum._StatStopped:
          prevstate = state;
          state = CosProcessStatusEnum.StatTerminating;         
          //notify process manager
          try
          {
            processManager.cosProcessTerminating(entityName,
                          CosTerminationCodeEnum.TermRequestedTerminate);
          }
          catch (Exception ex)
          {
            logger.warn("Couldn't update termination status: " + ex.getMessage());
          }         
          //stop current loop
          if (prevstate == CosProcessStatusEnum.StatRunningControl)
          {
            stopControl();
          }
          else if (prevstate == CosProcessStatusEnum.StatRunningMonitor)
          {
            stopMonitor();
          }       
          //quit the main loop
          keepRunning = false;
          break;
        default:
          //nothing to do here except reset the flag and notify process manager
          changeStatus(requestedState);
        }
      }
    } 
    //update status
    //shutting down Orb will stop all corba services.
    //this includes the ability to make a corba call.
    //thus, we have to update the status before we actually shutdown Orb
    changeStatus(CosProcessStatusEnum.StatStopped)

      //remove ior from persistence storage
    CorbaManager.removeIor(entityName);
     
    CorbaManager.shutdown();
    CorbaManager.cleanup();
  }
 
  public String getEntityName()
  {
    return entityName;
  }
 
  public void setLogger(Logger logger)
  {
    this.logger = logger;
      this.loggerWriter = new LoggerWriter(logger);
  }
 
  public Logger getLogger()
  {
    return logger;
  }
 
  public LoggerWriter getLoggerWriter()
  {
    return loggerWriter;
  }
   
  public CosProcessStatusEnum getStatus()
  {
    return state;
  }

  public void printConfiguration()
  {
    //Print out all configuration as visual feedback (runtime)
    Enumeration<Object> keysEnum = props.keys();
    //convert it into a string vector so that I can sort it
    Vector<String> keyList = new Vector<String>();
    while(keysEnum.hasMoreElements())
    {
      keyList.add((String)keysEnum.nextElement());
    }
    //sort it
    Collections.sort(keyList);
    //print out
    for(int i=0; i<keyList.size(); i++)
    {
      String key = keyList.get(i);
      String value = props.getProperty(key);
            //except if it contain the word "password" in its name
            if (key.toLowerCase().contains("password"))
            {
              logger.info("Property " + key + " = " + value.replaceAll(".", "*"));
            }
            else
            {
              logger.info("Property " + key + " = " + value);
            }
    }  //for each key
  }
 
  public int getCorbaPort()
  {
    return corbaPort;
  }
 
  public String getWorkingDirectory()
  {
    return workingDir;
  }
 
  public boolean isManagedAgent()
  {
    return hasManager;
  }
 
  public int getProcessManagerPort()
  {
    return managerPort;
  }
 
  public final Properties getRuntimeProperties()
  {
    return props;
  }
 
  /**
   * Get peer host name.
   * @return the peer host name.
   */
  public String getPeerName()
  {
    return peerName;
  }
 
  /**
   * Get peer port number.
   * @return the peer port number
   */
  public int getPeerPort()
  {
    return peerPort;
  }
 
//  @SuppressWarnings("unused")
//  protected void __wait(Object obj, boolean flag)
//  {
//    if (obj == null || !flag) return;
//    try {
//      obj.wait();
//    } catch (InterruptedException ie) {
//      //ignore
//      logger.warn("InterruptedException: " + ie.getMessage());
//    } 
//  }
// 
//  protected void __notify(Object obj)
//  {
//    if (obj == null) return;
//    try
//    {
//      obj.notifyAll();
//    }
//    catch (IllegalMonitorStateException ex)
//    {
//      logger.trace("Exception: " + ex.toString());
//    }
//  }
 
  protected int __parseInt(String text)
  {
    int retval = 0;
      try
      {
        retval = Integer.parseInt(text);
      }
      catch (NumberFormatException ne)
      {
        logger.trace("Exception: " + ne.toString());
     
      return retval;
  }
 
  /*
   * CORBA Subroutines
   */

  //ICosManagedProcess
  public void cosTerminate()
  {
    stop();
  }

  public void cosSetOperationMode(CosOperationModeEnum mode)
  {
    logger.info(entityName + ": received new operation mode " +
          CorbaHelper.OperationModeToString(mode));
   
    //ignore when terminating. client should never send conflicting request. but who knows?
    if (state == CosProcessStatusEnum.StatTerminating)
      return;
   
    //translate the operation mode into a process state
    if (mode == CosOperationModeEnum.OperControl)
    {
      if (state == CosProcessStatusEnum.StatGoingToControl ||
          state == CosProcessStatusEnum.StatRunningControl)
      {
        logger.info("Already in CONTROL mode.");
      }
      else
      {
        requestedState = CosProcessStatusEnum.StatRunningControl;
      }
    }
    else if (mode == CosOperationModeEnum.OperMonitor)
    {
      if (state == CosProcessStatusEnum.StatGoingToMonitor ||
          state == CosProcessStatusEnum.StatRunningMonitor)
      {
        logger.info("Already in MONITOR mode.");
      }
      else
      {
        requestedState = CosProcessStatusEnum.StatRunningMonitor;
      }
    }
   
    //trigger the main thread. only expect ONE main thread!!!
    if (thread != null)
    {
      thread.interrupt();
    }
  }

  public CosProcessStatusEnum cosGetStatus()
  {
    return state;
  }

  public CosOperationModeEnum cosGetOperationMode()
  {
    CosOperationModeEnum retval = null;
   
    if(state == CosProcessStatusEnum.StatRunningControl)
    {
      retval = CosOperationModeEnum.OperControl;
    }
    else if (state == CosProcessStatusEnum.StatRunningMonitor)
    {
      retval = CosOperationModeEnum.OperMonitor;
    }
    else
    {
      retval = CosOperationModeEnum.OperNotApplicable;
    }
    return retval;
  }

  public void cosSetParams(CosRunParamStruct[] paramSeq)
  {
    boolean reconfigure = false;
   
    for (int i=0; i<paramSeq.length; i++)
    { 
      logger.info(entityName + ": param " + paramSeq[i].name + " = " + paramSeq[i].value);
      props.setProperty(paramSeq[i].name, paramSeq[i].value);
     
      if (paramSeq[i].name.equalsIgnoreCase(LOG_LEVEL_KEY.value))
      {
        logger.info("Received a new log level: " + paramSeq[i].value);
        LoggerManager.setLogLevel(CorbaHelper.LogLevelStringToIntLogLevel(paramSeq[i].value));
      }
      else if (paramSeq[i].name.equalsIgnoreCase(LOG_FILE_KEY.value))
      {
        logger.info("Received a new log file: " + paramSeq[i].value);
        LoggerManager.setLogFile(paramSeq[i].value);
      }
      //from now on, ignore database parameters from process manager.
      //instead, load it from properties file!
//      else if (paramSeq[i].name.equalsIgnoreCase(CENTRAL_DB_TNSNAME_KEY.value)
//            || paramSeq[i].name.equalsIgnoreCase(CENTRAL_DB_USERNAME_KEY.value)
//            || paramSeq[i].name.equalsIgnoreCase(CENTRAL_DB_PASSWORD_KEY.value)
//            || paramSeq[i].name.equalsIgnoreCase(PRIMARY_DB_TNSNAME_KEY.value)
//            || paramSeq[i].name.equalsIgnoreCase(PRIMARY_DB_PASSWORD_KEY.value)
//            || paramSeq[i].name.equalsIgnoreCase(PRIMARY_DB_USERNAME_KEY.value)
//            || paramSeq[i].name.equalsIgnoreCase(STANDBY_DB_TNSNAME_KEY.value)
//            || paramSeq[i].name.equalsIgnoreCase(STANDBY_DB_USERNAME_KEY.value)
//            || paramSeq[i].name.equalsIgnoreCase(STANDBY_DB_PASSWORD_KEY.value))
//      {
//        reconfigure = true;
//      }
      reconfigure = true;
    }
   
    //configure the application only if it is not yet configured.
    //prevent reconfiguration!!!
    if (reconfigure)
    {
      if (isConfigured)
      {
        logger.warn("Already configured. Will not reconfigure!");
      }
      else
      {
        //this is actually very dangerous.
        //chances are, this will take very long to complete.
        //this cosSetParam might then timeout throwing CORBA::TIMEOUT exception
        configureApplication();
      }
    }  //if (reconfigure)
  //cosSetParams()

  public CosRunParamStruct[] cosGetParams()
  {
    CosRunParamStruct[] retval = new CosRunParamStruct[props.size()];

    Enumeration<Object> iter = props.keys();
    String key = null, value = null;
    int i = 0;
    while (iter.hasMoreElements())
    {
      key = (String) iter.nextElement();
      value = (String) props.getProperty(key);
      if (value != null)
      {
        retval[i].name = key;
        retval[i].value = value;
      }
      i++;
    }
   
    return retval;
  }

  public void cosSetBaseWeightage(short inWeightage)
  {
    this.weightage = inWeightage;
  }
 
  public short cosGetWeightage()
  {
    return this.weightage;
  }

  public String cosGetStatusString()
  {
    //list down all jobs and trigger
    return getStatusString();
  }

  public void cosSetLogLevel(CosLogLevelEnum loglevel)
  {
    logger.info("New log level: " + CorbaHelper.LogLevelToString(loglevel));
    Level level = CorbaHelper.LogLevelToIntLogLevel(loglevel);
    //set log level globally
    LoggerManager.setLogLevel(level);
  }

  public void cosSetLogLevelDetail(String loggername, CosLogLevelEnum loglevel)
  {
    logger.info("Log level for logger " + loggername + " changed. New log level: " +
            CorbaHelper.LogLevelToString(loglevel));
    Level level = CorbaHelper.LogLevelToIntLogLevel(loglevel);
    LoggerManager.setLogLevelDetail(loggername, level);
  }

  //ICosMonitoredThread
  public long cosGetProcessId()
  {
    if (pid == 0)
    {
      pid = (int) Native.getpid();
    }
    return pid;
  }

  //override this to provide more specific process type
  public CosProcessTypeEnum cosGetProcessType()
  {
    return CosProcessTypeEnum.ProcThread;
  }
 
  public void cosPoll() throws CosPollException
  {
    //PollException only need to be called when we need to inform caller/poller
    //that we have some internal problem eventhough we are still running.
    return;
  }
 
  public void cosPollControl() throws CosPollException, CosPollNotInModeException
  {
    if (state != CosProcessStatusEnum.StatRunningControl)
    {
      throw new CosPollNotInModeException();
    }
    return;
  }

  public void cosPollMonitor() throws CosPollException, CosPollNotInModeException
  {
    if (state != CosProcessStatusEnum.StatRunningMonitor)
    {
      throw new CosPollNotInModeException();
    }
    return;
  }
 
  /**
   * Thread for polling the primary peer (when running as stand-alone)
   * @author Yoga
   */
  class PrimaryPollerThread implements Runnable
  {
    private Thread   thread_ = null;
    private boolean keepThreadRunning_ = false;
   
    /**
     * Start the thread.
     */
    public void start()
    {
      //avoid running more than once
      if (thread_ != null && thread_.isAlive())
      {
        return;
      }
      //create a new plan master thread
      thread_ = new Thread(this);
      //finally, start the thread and run the plan master
      thread_.start();
    }
   
    /**
     * Stop the current thread.
     *
     * @param millis  - wait for termination, in msec.
     */
    public void stop(long millis)
    {
      if (thread_ == null)
      {
        return;
      }
      //stop the listen thread
      keepThreadRunning_ = false;
      try
      {
        thread_.interrupt();
        if (millis > 0)
          thread_.join(millis);
      }
      catch (InterruptedException ie)
      {
        //ignore
      }
      thread_ = null;
    }
   
    /**
     * Polling loop
     */
    public void run()
    {
      // TODO Auto-generated method stub
      //just to make sure that we are very careful
      if (peerProcess == null)
      {
        connect_to_peer_process();
      }
     
      //if we still can not get valid reference to peer process, just quit
      if (peerProcess == null)
      {
        return;
      }
     
      keepThreadRunning_ = true;
     
      //keep polling the primary.
      boolean status = true;
      while (keepThreadRunning_ && status)
      {
        status = false;
        for (int i=0; i<CORBA_ERROR_THRESHOLD && !status; i++)
        {
          try
          {
            peerProcess.cosPollControl();
            status = true;
          }
          catch (Exception ex)
          {
            //fail to confirm that primary is in control.
          }
        }  //end for-loop
       
        if (status)
        {
          //sleep for a while
          try
          {
            Thread.sleep(peerSyncRate);
          }
          catch (InterruptedException ie)
          {
            //ignore
          }
        }
      }  //end while(status)
     
      keepThreadRunning_ = false;
     
      //primary server switch to monitor or it has terminated.
      //switch ourselves to control
      requestedState = CosProcessStatusEnum.StatRunningControl;
     
      //reset the reference to peer
      peerProcess = null;
    }
  }
 
  /**
   * Thread for notifying the standby peer (when running as stand-alone)
   * @author Yoga
   */
  class StandbyNotifierThread implements Runnable
  {
    private Thread   thread_ = null;
    private boolean keepThreadRunning_ = false;
   
    /**
     * Start the thread.
     */
    public void start()
    {
      //avoid running more than once
      if (thread_ != null && thread_.isAlive())
      {
        return;
      }
      //create a new plan master thread
      thread_ = new Thread(this);
      //finally, start the thread and run the plan master
      thread_.start();
    }
   
    /**
     * Stop the current thread.
     *
     * @param millis  - wait for termination, in msec.
     */
    public void stop(long millis)
    {
      if (thread_ == null)
      {
        return;
      }
      //stop the listen thread
      keepThreadRunning_ = false;
      try
      {
        thread_.interrupt();
        if (millis > 0)
          thread_.join(millis);
      }
      catch (InterruptedException ie)
      {
        //ignore
      }
      thread_ = null;
    }
   
    /**
     * Notification loop
     */
    public void run()
    {
      //just to make sure that we are very careful
      if (peerProcess == null)
      {
        connect_to_peer_process();
      }
     
      //if we still can not get valid reference to peer process, just quit
      if (peerProcess == null)
      {
        return;
      }
     
      keepThreadRunning_ = true;
     
      //keep polling the primary.
      boolean status = true;
      while (keepThreadRunning_ && status)
      {
        status = false;
        for (int i=0; i<CORBA_ERROR_THRESHOLD && !status; i++)
        {
          try
          {
            peerProcess.cosSetOperationMode(CosOperationModeEnum.OperControl);
            status = true;
          }
          catch (Exception ex)
          {
            //fail to notify the standby that we are in control.
          }
        }  //end for-loop
       
        if (status)
        {
          //sleep for a while
          try
          {
            Thread.sleep(peerSyncRate);
          }
          catch (InterruptedException ie)
          {
            //ignore
          }
        }
      }  //end while(status)
     
      keepThreadRunning_ = false;
     
      //either we are terminating or the peer process has terminated.
      //in any case, reset the peer reference
      peerProcess = null;
    }
  }
}
TOP

Related Classes of tcg.syscontrol.ManagedProcess$PrimaryPollerThread

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.