/**
   * Checks that all named sessions given to the listener are still running (and accessible). If
   * they are not, new sessions are started as needed. This method is synchronized to prevent
   * multiple threads from each starting their own CM Synergy sessions.
   *
   * @param ccmExe The CM Synergy command line executable
   * @param sessionFile The CM Synergy session map file
   * @param sessions A list of monitored CM Synergy sessions
   * @throws CruiseControlException if something breaks
   */
  private static synchronized void checkSessions(
      final String ccmExe, final File sessionFile, final List<CMSynergySession> sessions)
      throws CruiseControlException {
    LOG.debug("Using persisted data from " + sessionFile.getAbsolutePath());

    // Load the persisted session information from file
    final Properties sessionMap;
    try {
      sessionMap = Util.loadPropertiesFromFile(sessionFile);
    } catch (IOException e) {
      throw new CruiseControlException(e);
    }

    // Get a list of currently running CM Synergy sessions
    final ManagedCommandline cmd = new ManagedCommandline(ccmExe);
    cmd.createArgument("status");
    String availableSessions;
    try {
      cmd.execute();
      cmd.assertExitCode(0);
      availableSessions = cmd.getStdoutAsString();
    } catch (Exception e) {
      LOG.warn("CM Synergy failed to provide a list of valid sessions.", e);
      availableSessions = "";
    }

    // Check each monitored session in turn
    for (final CMSynergySession session : sessions) {
      final String name = session.getName();
      final String id = sessionMap.getProperty(name);
      LOG.info("Checking " + name + ".");
      if (id == null || availableSessions.indexOf(id) < 0) {
        // Start a new session and record the ID in the map
        final String newID = startSession(ccmExe, session);
        if (newID != null) {
          LOG.info("Started CM Synergy session \"" + newID + "\".");
          sessionMap.setProperty(name, newID);
        }
      } else {
        LOG.info("Using existing session \"" + id + "\".");
      }
    }

    // Update the persisted session information
    try {
      Util.storePropertiesToFile(sessionMap, "CM Synergy session map", sessionFile);
    } catch (IOException e) {
      throw new CruiseControlException(e);
    }
  }
  /**
   * Launches a new CM Synergy command line session
   *
   * @param ccmExe The CM Synergy command line executable
   * @param session The session information
   * @return stdout from new session.
   */
  private static String startSession(final String ccmExe, final CMSynergySession session) {

    LOG.info("Starting a new CM Synergy session for \"" + session.getName() + "\".");

    // Create CM Synergy startup command
    final ManagedCommandline cmd = new ManagedCommandline(ccmExe);
    cmd.createArgument("start");
    cmd.createArgument("-q");
    cmd.createArgument("-nogui");
    cmd.createArgument("-m");
    cmd.createArguments("-d", session.getDatabase());
    cmd.createArguments("-r", session.getRole());
    cmd.createArguments("-n", session.getUser());
    cmd.createArguments("-pw", session.getPassword());
    if (session.getHost() != null) {
      cmd.createArguments("-h", session.getHost());
    }

    if (session.isRemoteClient()) {
      LOG.debug("Connecting in remote-client mode");
      cmd.createArgument("-rc");
    } else {
      LOG.debug("Connecting in local mode");
    }

    try {
      cmd.execute();
      cmd.assertExitCode(0);
    } catch (Exception e) {
      LOG.error("Could not start a CM Synergy session for " + session.getName(), e);
      return null;
    }

    return cmd.getStdoutAsString().trim();
  }