/**
   * Create a database environment to represent the data in envHome. dbHome. Properties from the
   * je.properties file in that directory are used to initialize the system wide property bag.
   * Properties passed to this method are used to influence the open itself.
   *
   * @param envHome absolute path of the database environment home directory
   * @param envConfig
   * @throws DatabaseException on all other failures
   */
  public EnvironmentImpl(File envHome, EnvironmentConfig envConfig) throws DatabaseException {

    try {
      this.envHome = envHome;
      envState = DbEnvState.INIT;

      /* Set up configuration parameters */
      configManager = new DbConfigManager(envConfig);
      configObservers = new ArrayList();
      addConfigObserver(this);

      /*
       * Decide on memory budgets based on environment config params and
       * memory available to this process.
       */
      memoryBudget = new LogBufferBudget(this, configManager);

      /*
       * Set up debug logging. Depending on configuration, add handlers,
       * set logging level.
       */
      // envLogger = initLogger(envHome);
      /*
       * Essential services. These must exist before recovery.
       */
      hook_readProperties(configManager);
      forcedYield = configManager.getBoolean(EnvironmentParams.ENV_FORCED_YIELD);
      isNoLocking = !(configManager.getBoolean(EnvironmentParams.ENV_INIT_LOCKING));
      isReadOnly = configManager.getBoolean(EnvironmentParams.ENV_RDONLY);

      fileManager = new FileManager(this, envHome, isReadOnly);
      if (!envConfig.getAllowCreate() && !fileManager.filesExist()) {
        throw new DatabaseException(
            "Enviroment creation isn't allowed, "
                + " but there is no pre-existing "
                + " environment in "
                + envHome);
      }

      logManager = new SyncedLogManager(this, isReadOnly);

      inMemoryINs = new INList(this);
      txnManager = new TxnManager(this);

      /*
       * Make sure that either log-size-based or time-based checkpointing is
       * enabled.
       */
      checkpointer = new Checkpointer(this);
      cleaner = new Cleaner(this, "Cleaner");
      /*
       * Daemons are always made here, but only started after recovery. We
       * want them to exist so we can call them programatically even if
       * the daemon thread is not started.
       */
      createDaemons();

      /*
       * Recovery will recreate the dbMapTree from the log if it exists.
       */
      dbMapTree = new DbTree(this);

      referenceCount = 0;

      /*
       * Do not do recovery and start daemons if this environment is for a
       * utility.
       */
      if (configManager.getBoolean(EnvironmentParams.ENV_RECOVERY)) {

        /*
         * Run recovery. Note that debug logging to the database log is
         * disabled until recovery is finished.
         */
        try {
          RecoveryManager recoveryManager = new RecoveryManager(this);
          lastRecoveryInfo = recoveryManager.recover(isReadOnly);
        } finally {
          try {
            /* Flush to get all exception tracing out to the log. */
            logManager.flush();
            fileManager.clear();
          } catch (IOException e) {
            throw new DatabaseException(e.getMessage());
          }
        }
      } else {
        isReadOnly = true;
        noComparators = true;
      }

      /* Start daemons after recovery. */
      runOrPauseDaemons(configManager);

      /*
       * Cache a few critical values. We keep our timeout in millis
       * instead of microseconds because Object.wait takes millis.
       */
      lockTimeout = PropUtil.microsToMillis(configManager.getLong(EnvironmentParams.LOCK_TIMEOUT));
      txnTimeout = PropUtil.microsToMillis(configManager.getLong(EnvironmentParams.TXN_TIMEOUT));

      /* Mark as open. */
      open();
    } catch (DatabaseException e) {

      /* Release any environment locks if there was a problem. */
      if (fileManager != null) {
        try {
          fileManager.close();
        } catch (IOException IOE) {

          /*
           * Klockwork - ok Eat it, we want to throw the original
           * exception.
           */
        }
      }
      throw e;
    }
  }