public void run() {
    EventLog.log("Starting nanny for \"" + getServerDesc() + "\"");
    setStartedAndRunning(true);
    NDC.push(getServerDesc());
    try {
      // Nanny villages
      while (isKeepRunning()) {
        util.getConsole().checkPause();
        TimeWhenRunnable firstTimeWhenRunnable = null;
        sharpTimeWhenRunnable = null;
        if ((messageMode != null) && !messageMode.equals("false")) {
          // check messages before for commands including resume
          int msgRead = messageReader.readMessages(util, loginUrl, messageMode);
          log.info("Messages Done next " + msgRead);
        }
        // check if still enabled
        if (!suspended) {
          // Loop all enabled villages
          EventLog.log("Checking Villages");
          firstTimeWhenRunnable = processVillages();
          log.info("Villages done");
          // gac - move servers below villages
          // Loop all enabled server strategies
          TimeWhenRunnable serverStrategiesTimeWhenRunnable = processServerStrategies();
          if (serverStrategiesTimeWhenRunnable.before(firstTimeWhenRunnable)) {
            log.debug("ServerStrategy Earlier than villages");
            firstTimeWhenRunnable = serverStrategiesTimeWhenRunnable;
          }
          // gac add report processing - exists as server strategy to run periodically
          // but this will run at the end of every active period
          // count if outstanding after this read
          int moreRead = 0;
          if ((reportMode != null) && !reportMode.equals("false")) {
            // set the reporting mode and check reports
            // ReportMessageReader.getInstance().setReportsMode(reportMode);
            // EventLog.log(loginUrl+" about to read reportMode="+reportMode);
            moreRead = reportReader.readReports(util, loginUrl, reportMode);
            log.info("Server Reports done");
          }
          // read messages again
          if ((messageMode != null) && !messageMode.equals("false")) {
            // check messages
            int msgRead = messageReader.readMessages(util, loginUrl, messageMode);
            log.info("Messages at end done next " + msgRead);
            // read more if consuming all message not just scanning titles for commands
            // if (!messageMode.equalsIgnoreCase("titles")) {
            if (!messageMode.equalsIgnoreCase(messageReader.getScanMode())) {
              moreRead += msgRead;
            }
          }
          if (moreRead != 0) {
            // still more to read
            firstTimeWhenRunnable = TimeWhenRunnable.NOW; // Re-run immediately
            log.debug("Villages will be run again because more reports to read (" + moreRead + ")");
          } else {
            // no new reports left
          }
          if (isConfigurationChanged()) {
            // Update configuration
            updateConfig(getNextConfig());
            EventLog.log("evt.configReloadDone", this.getClass());
            this.nextConfig = null; // No need to synchronize here
            firstTimeWhenRunnable = TimeWhenRunnable.NOW; // Re-run immediately
            log.debug("Villages will be run again because of configuration update");
          }
        } else {
          // suspended
          log.debug("All Strategies Suspended");
          firstTimeWhenRunnable =
              new TimeWhenRunnable(
                  System.currentTimeMillis() + SUSPENDCHECK * Util.MILLI_MINUTE); // try again later
        }
        boolean sharp = false;
        long milliPause = 0;
        long milliSharp = -1L;
        if (firstTimeWhenRunnable != null) {
          log.debug("Earliest Strategy @ " + firstTimeWhenRunnable);
          sharp = firstTimeWhenRunnable.isSharp();
          milliPause = firstTimeWhenRunnable.getTime() - System.currentTimeMillis();
          if (milliPause < 0) {
            milliPause = 0;
          }
          int pauseLimit = config.getInt("/@pauseLimit", 86400) * 1000; //  Max 24h
          if (milliPause > pauseLimit) {
            log.info("Limiting pause from " + milliPause + " to " + pauseLimit);
            milliPause = pauseLimit;
          }
          if (sharpTimeWhenRunnable != null) {
            log.debug("Earliest sharp Strategy @ " + sharpTimeWhenRunnable);
            milliSharp = sharpTimeWhenRunnable.getTime() - System.currentTimeMillis();
            if (milliSharp < 0) {
              milliSharp = 0;
            }
          }
          log.debug(
              "Next available Action in "
                  + Util.milliToTimeString(milliPause)
                  + (sharp ? " sharp: " + Util.milliToTimeString(milliSharp) : ""));
        }

        if (isKeepRunning()) {
          util.userPause(milliPause, sharp, milliSharp);
          util.shortestPause(sharp); // Just to be safe
        }
      }
    } catch (Exception e) {
      EventLog.log(e.getMessage());
      log.error("", e);
      setEnabledAndStartStop(false);
    } finally {
      NDC.remove(); // not pop()
    }
  }
 private void updateConfig(SubnodeConfiguration newServerConfig)
     throws InvalidConfigurationException {
   log.debug("Updating server config for " + getServerId());
   this.config = newServerConfig;
   this.util.setServerConfig(newServerConfig);
   this.util.setServer(this);
   this.util.setServerId(user, getServerId()); // set server id for use in report processing
   // check if reading reports from overall server
   reportMode = newServerConfig.getString("/reports", null); // simple format to just read
   reportMode =
       newServerConfig.getString(
           "/reports/@enabled", reportMode); // complex config with processing
   if (reportMode != null) {
     SubnodeConfiguration reportConfig = newServerConfig.configurationAt("/reports");
     reportMode =
         reportConfig.getString("/output/@format", reportMode); // complex config with processing
     EventLog.log(loginUrl + " reportMode=" + reportMode);
     reportReader.setReportsMode(reportMode, reportConfig);
   }
   messageMode = newServerConfig.getString("/messages", null);
   messageMode = newServerConfig.getString("/messages/@enabled", messageMode);
   // note once enabled have to turn off by setting false or will not call to disable
   if (messageMode != null) {
     SubnodeConfiguration messageConfig = newServerConfig.configurationAt("/messages");
     String messageText = messageConfig.getString("/@commands", null);
     EventLog.log(
         loginUrl + " messageMode=" + messageMode + " Command Text (" + messageText + ")");
     // use same as mode or a separate param?
     messageReader.setMessagesMode(messageMode, messageConfig);
   }
   setTribeType(newServerConfig);
   //
   // Update villages
   // When a village is disabled or removed from the configuration, it is deleted from
   // this.villages
   List<Village> deletableVillages = new ArrayList<Village>(villages.values());
   // Loop all enabled villages
   List<SubnodeConfiguration> villageConfigs =
       newServerConfig.configurationsAt("/village[@enabled='true']");
   for (SubnodeConfiguration villageConfig : villageConfigs) {
     String id = Village.getIdFromConfig(villageConfig); // Either uid or url
     Village village = this.villages.get(id);
     if (village == null) {
       // New village
       village = new Village(util, villageConfig, strategyStatus);
       this.villages.put(id, village);
     } else {
       // Village already exists
       village.updateConfig(villageConfig, util);
       deletableVillages.remove(village); // This village is still enabled	
     }
   }
   // Removing deleted or disabled villages
   for (Village village : deletableVillages) {
     this.villages.remove(village.getId());
     village.terminate();
   }
   //
   // Update server strategies
   serverStrategyList =
       updateStrategies(
           "/serverStrategy[@enabled='true']", newServerConfig, serverStrategies, null);
 }