/*
   * Clear as many resources as possible, even in the face of an environment
   * that has received a fatal error, in order to support reopening the
   * environment in the same JVM.
   */
  public synchronized void closeAfterRunRecovery() throws DatabaseException {

    try {
      shutdownDaemons();
    } catch (InterruptedException IE) {
      /* Klockwork - ok */
    }

    try {
      fileManager.clear();
    } catch (Exception e) {
      /* Klockwork - ok */
    }

    try {
      fileManager.close();
    } catch (Exception e) {
      /* Klockwork - ok */
    }

    DbEnvPool.getInstance().remove(envHome);
  }
  private void doClose(boolean doCheckpoint) throws DatabaseException {

    StringBuffer errors = new StringBuffer();

    try {
      // refined trace Tracer.trace(Level.FINE, this, "Close of
      // environment " + envHome + " started");

      try {
        envState.checkState(DbEnvState.VALID_FOR_CLOSE, DbEnvState.CLOSED);
      } catch (DatabaseException DBE) {
        throw DBE;
      }

      /*
       * Begin shutdown of the deamons before checkpointing. Cleaning
       * during the checkpoint is wasted and slows down the checkpoint.
       */
      requestShutdownDaemons();

      /* Checkpoint to bound recovery time. */
      if (doCheckpoint
          && !isReadOnly
          && (envState != DbEnvState.INVALID)
          && logManager.getLastLsnAtRecovery() != fileManager.getLastUsedLsn()) {

        /*
         * Force a checkpoint. Don't allow deltas (minimize recovery
         * time) because they cause inefficiencies for two reasons: (1)
         * recovering BINDeltas causes extra random I/O in order to
         * reconstitute BINS, which can greatly increase recovery time,
         * and (2) logging deltas during close causes redundant logging
         * by the full checkpoint after recovery.
         */
        CheckpointConfig ckptConfig = new CheckpointConfig();
        ckptConfig.setForce(true);
        ckptConfig.setMinimizeRecoveryTime(true);
        try {
          invokeCheckpoint(
              ckptConfig,
              false, // flushAll
              "close");
        } catch (DatabaseException IE) {
          errors.append("\nException performing checkpoint: ");
          errors.append(IE.toString()).append("\n");
        }
      }

      try {
        shutdownDaemons();
      } catch (InterruptedException IE) {
        errors.append("\nException shutting down daemon threads: ");
        errors.append(IE.toString()).append("\n");
      }

      /* Flush log. */
      // refined trace Tracer.trace(Level.FINE, this, "Env " + envHome + "
      // daemons shutdown");
      try {
        logManager.flush();
      } catch (DatabaseException DBE) {
        errors.append("\nException flushing log manager: ");
        errors.append(DBE.toString()).append("\n");
      }

      try {
        fileManager.clear();
      } catch (IOException IOE) {
        errors.append("\nException clearing file manager: ");
        errors.append(IOE.toString()).append("\n");
      } catch (DatabaseException DBE) {
        errors.append("\nException clearing file manager: ");
        errors.append(DBE.toString()).append("\n");
      }

      try {
        fileManager.close();
      } catch (IOException IOE) {
        errors.append("\nException clearing file manager: ");
        errors.append(IOE.toString()).append("\n");
      } catch (DatabaseException DBE) {
        errors.append("\nException clearing file manager: ");
        errors.append(DBE.toString()).append("\n");
      }

      try {
        inMemoryINs.clear();
      } catch (DatabaseException DBE) {
        errors.append("\nException closing file manager: ");
        errors.append(DBE.toString()).append("\n");
      }

      hook_afterDoClose(errors);
    } finally {
      envState = DbEnvState.CLOSED;
    }

    if (errors.length() > 0 && savedInvalidatingException == null) {

      /* Don't whine again if we've already whined. */
      throw new RunRecoveryException(this, errors.toString());
    }
  }
  /**
   * 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;
    }
  }