/**
   * logs warning message if pid file exists
   *
   * @param confFilePath
   * @return true if pid file exists
   */
  private static boolean checkPidFile(Path confFilePath) {
    if (OSChecker.isWindows()) {
      return false;
    }

    // pid file name include the hash of the configuration file so that
    // for each configuration we can have just one instance running
    Path pidFilePath = FileUtils.getPidFilePath(FileUtils.getFileAbsoultePathHash(confFilePath));

    if (Files.exists(pidFilePath)) {
      LOGGER.warn(
          "Found pid file! If this instance is already "
              + "running, startup will fail with a BindException");

      return true;
    }

    return false;
  }
  /**
   * Startups the RESTHeart server
   *
   * @param confFilePath the path of the configuration file
   */
  public static void startup(final Path confFilePath) {
    try {
      configuration = FileUtils.getConfiguration(confFilePath, false);
    } catch (ConfigurationException ex) {
      if (RESTHEART_VERSION != null) {
        LOGGER.info(
            ansi().fg(RED).bold().a("RESTHeart").reset().toString() + " version {}",
            RESTHEART_VERSION);
      }

      LOGGER.error(ex.getMessage() + ", exiting...", ex);
      stopServer(false);
      System.exit(-1);
    }

    startServer(false);
  }
  private static void stopServer(boolean silent, boolean removePid) {
    if (!silent) {
      LOGGER.info("Stopping RESTHeart...");
    }

    if (shutdownHandler != null) {
      if (!silent) {
        LOGGER.info("Waiting for pending request to complete (up to 1 minute)...");
      }
      try {
        shutdownHandler.shutdown();
        shutdownHandler.awaitShutdown(60 * 1000); // up to 1 minute
      } catch (InterruptedException ie) {
        LOGGER.error("Error while waiting for pending request to complete", ie);
      }
    }

    if (server != null) {
      if (!silent) {
        LOGGER.info("Stopping the Undertow server...");
      }
      try {
        server.stop();
      } catch (Throwable t) {
        LOGGER.error("Error stopping the Undertow server", t);
      }
    }

    try {
      if (!silent) {
        LOGGER.info("Flushing and closing the MongoDB client...");
      }
      if (MongoDBClientSingleton.isInitialized()) {
        MongoClient client = MongoDBClientSingleton.getInstance().getClient();
        client.fsync(false);
        client.close();
      }
    } catch (Throwable t) {
      LOGGER.error("Error flushing and closing the MongoDB client", t);
    }

    Path pidFilePath = FileUtils.getPidFilePath(FileUtils.getFileAbsoultePathHash(CONF_FILE_PATH));

    if (removePid && pidFilePath != null) {
      if (!silent) {
        LOGGER.info("Removing the pid file {}", pidFilePath.toString());
      }
      try {
        Files.deleteIfExists(pidFilePath);
      } catch (IOException ex) {
        LOGGER.error("Can't delete pid file {}", pidFilePath.toString(), ex);
      }
    }

    if (!silent) {
      LOGGER.info("Cleaning up temporary directories...");
    }
    TMP_EXTRACTED_FILES
        .keySet()
        .forEach(
            k -> {
              try {
                ResourcesExtractor.deleteTempDir(k, TMP_EXTRACTED_FILES.get(k));
              } catch (URISyntaxException | IOException ex) {
                LOGGER.error(
                    "Error cleaning up temporary directory {}",
                    TMP_EXTRACTED_FILES.get(k).toString(),
                    ex);
              }
            });

    if (!silent) {
      LOGGER.info(ansi().fg(GREEN).bold().a("RESTHeart stopped").reset().toString());
    }
  }
  private static void startServer(boolean fork) {
    LOGGER.info("Starting " + ansi().fg(RED).bold().a("RESTHeart").reset().toString());

    if (RESTHEART_VERSION != null) {
      LOGGER.info("version {}", RESTHEART_VERSION);
    }

    Path pidFilePath = FileUtils.getPidFilePath(FileUtils.getFileAbsoultePathHash(CONF_FILE_PATH));

    boolean pidFileAlreadyExists = false;

    if (!OSChecker.isWindows() && pidFilePath != null) {
      pidFileAlreadyExists = checkPidFile(CONF_FILE_PATH);
    }

    logLoggingConfiguration(fork);

    LOGGER.debug(
        "Initializing MongoDB connection pool to {} with options {}",
        configuration.getMongoUri().getHosts(),
        configuration.getMongoUri().getOptions());

    try {
      MongoDBClientSingleton.init(configuration);
      // force setup
      MongoDBClientSingleton.getInstance();
      LOGGER.info("MongoDB connection pool initialized");
      LOGGER.info("MongoDB version {}", MongoDBClientSingleton.getServerVersion());
    } catch (Throwable t) {
      LOGGER.error("Error connecting to MongoDB. exiting..", t);
      stopServer(false, !pidFileAlreadyExists);
      System.exit(-1);
    }

    try {
      startCoreSystem();
    } catch (Throwable t) {
      LOGGER.error("Error starting RESTHeart. Exiting...", t);
      stopServer(false, !pidFileAlreadyExists);
      System.exit(-2);
    }

    Runtime.getRuntime()
        .addShutdownHook(
            new Thread() {
              @Override
              public void run() {
                stopServer(false);
              }
            });

    // create pid file on supported OSes
    if (!OSChecker.isWindows() && pidFilePath != null) {
      FileUtils.createPidFile(pidFilePath);
    }

    // log pid file path on supported OSes
    if (!OSChecker.isWindows() && pidFilePath != null) {
      LOGGER.info("Pid file {}", pidFilePath);
    }

    LOGGER.info(ansi().fg(GREEN).bold().a("RESTHeart started").reset().toString());
  }
 /**
  * Startups the RESTHeart server
  *
  * @param confFilePath the path of the configuration file
  */
 public static void startup(final String confFilePath) {
   startup(FileUtils.getFileAbsoultePath(confFilePath));
 }
  /**
   * main method
   *
   * @param args command line arguments
   */
  public static void main(final String[] args) {
    CONF_FILE_PATH = FileUtils.getConfigurationFilePath(args);

    try {
      // read configuration silently, to avoid logging before initializing the logger
      configuration = FileUtils.getConfiguration(args, true);
    } catch (ConfigurationException ex) {
      LOGGER.info("Starting " + ansi().fg(RED).bold().a("RESTHeart").reset().toString());

      if (RESTHEART_VERSION != null) {
        LOGGER.info("version {}", RESTHEART_VERSION);
      }

      LOGGER.error(ex.getMessage() + ", exiting...", ex);
      stopServer(false);
      System.exit(-1);
    }

    if (!hasForkOption(args)) {
      startServer(false);
    } else {
      if (OSChecker.isWindows()) {
        LOGGER.info("Starting " + ansi().fg(RED).bold().a("RESTHeart").reset().toString());

        if (RESTHEART_VERSION != null) {
          LOGGER.info("version {}", RESTHEART_VERSION);
        }

        LOGGER.error("Fork is not supported on Windows");

        LOGGER.info(ansi().fg(GREEN).bold().a("RESTHeart stopped").reset().toString());

        System.exit(-1);
      }

      // RHDaemon only works on POSIX OSes
      final boolean isPosix =
          FileSystems.getDefault().supportedFileAttributeViews().contains("posix");

      if (!isPosix) {
        LOGGER.info("Unable to fork process, " + "this is only supported on POSIX compliant OSes");

        stopServer(false);
        System.exit(-1);
      }

      RHDaemon d = new RHDaemon();

      if (d.isDaemonized()) {
        try {
          d.init();
          LOGGER.info("Forked process: {}", LIBC.getpid());
          initLogging(args, d);
        } catch (Throwable t) {
          LOGGER.error("Error staring forked process", t);
          stopServer(false, false);
          System.exit(-1);
        }

        startServer(true);
      } else {
        initLogging(args, d);

        try {
          LOGGER.info("Starting " + ansi().fg(RED).bold().a("RESTHeart").reset().toString());

          if (RESTHEART_VERSION != null) {
            LOGGER.info("version {}", RESTHEART_VERSION);
          }

          logLoggingConfiguration(true);

          d.daemonize();
        } catch (Throwable t) {
          LOGGER.error("Error forking", t);
          stopServer(false, false);
          System.exit(-1);
        }
      }
    }
  }