public void init() throws BabuDBException {

    // load persisted snapshots from disk
    for (Entry<String, DatabaseInternal> entry :
        dbs.getDatabaseManager().getDatabasesInternal().entrySet()) {

      final File snapDir = new File(dbs.getConfig().getBaseDir(), entry.getKey() + "/snapshots");
      if (snapDir.exists()) {

        Map<String, Snapshot> snapMap = new HashMap<String, Snapshot>();
        snapshotDBs.put(entry.getKey(), snapMap);

        boolean compressed = entry.getValue().getLSMDB().getIndex(0).isCompressed();
        boolean mmaped = entry.getValue().getLSMDB().getIndex(0).isMMapEnabled();

        String[] snapshots = snapDir.list();
        for (String snapName : snapshots) {
          BabuDBView view =
              new DiskIndexView(
                  snapDir + "/" + snapName, entry.getValue().getComparators(), compressed, mmaped);
          snapMap.put(snapName, new Snapshot(view, dbs));
        }
      }
    }
  }
  @Override
  public void deletePersistentSnapshot(String dbName, String snapshotName) throws BabuDBException {

    BabuDBRequestResultImpl<Object> result =
        new BabuDBRequestResultImpl<Object>(dbs.getResponseManager());
    dbs.getTransactionManager()
        .makePersistent(
            dbs.getDatabaseManager().createTransaction().deleteSnapshot(dbName, snapshotName),
            result);
    result.get();
  }
  /* (non-Javadoc)
   * @see org.xtreemfs.babudb.api.SnapshotManager#createPersistentSnapshot(java.lang.String, org.xtreemfs.babudb.snapshots.SnapshotConfig)
   */
  @Override
  public void createPersistentSnapshot(String dbName, SnapshotConfig snap) throws BabuDBException {

    // synchronously executing the request
    BabuDBRequestResultImpl<Object> result =
        new BabuDBRequestResultImpl<Object>(dbs.getResponseManager());
    dbs.getTransactionManager()
        .makePersistent(
            dbs.getDatabaseManager().createTransaction().createSnapshot(dbName, snap), result);
    result.get();
  }
 /* (non-Javadoc)
  * @see org.xtreemfs.babudb.api.dev.SnapshotManagerInternal#getSnapshotDir(java.lang.String,
  *          java.lang.String)
  */
 @Override
 public String getSnapshotDir(String dbName, String snapshotName) {
   return dbs.getConfig().getBaseDir()
       + dbName
       + "/"
       + SnapshotManagerImpl.SNAP_DIR
       + "/"
       + (snapshotName == null ? "" : snapshotName);
 }
  /* (non-Javadoc)
   * @see org.xtreemfs.babudb.api.dev.SnapshotManagerInternal#snapshotComplete(java.lang.String,
   *          org.xtreemfs.babudb.snapshots.SnapshotConfig)
   */
  @Override
  public void snapshotComplete(String dbName, SnapshotConfig snap) throws BabuDBException {

    // as soon as the snapshot has been completed, replace the entry in the
    // snapshot DB map with a disk index-based BabuDB instance if necessary
    synchronized (snapshotDBs) {
      DatabaseInternal db = dbs.getDatabaseManager().getDatabase(dbName);
      boolean compressed = db.getLSMDB().getIndex(0).isCompressed();
      boolean mmaped = db.getLSMDB().getIndex(0).isMMapEnabled();

      Snapshot s = snapshotDBs.get(dbName).get(snap.getName());
      s.setView(
          new DiskIndexView(
              getSnapshotDir(dbName, snap.getName()),
              dbs.getDatabaseManager().getDatabase(dbName).getComparators(),
              compressed,
              mmaped));
    }
  }
  /* (non-Javadoc)
   * @see org.xtreemfs.babudb.api.dev.SnapshotManagerInternal#deleteAllSnapshots(java.lang.String)
   */
  @Override
  public void deleteAllSnapshots(String dbName) throws BabuDBException {

    final Map<String, Snapshot> snapMap = snapshotDBs.get(dbName);
    if (snapMap != null) {

      for (Entry<String, Snapshot> snap : snapMap.entrySet()) {

        // shut down the view
        snap.getValue().shutdown();

        // if a snapshot materialization request is currently in the
        // checkpointer queue, remove it
        dbs.getCheckpointer().removeSnapshotMaterializationRequest(dbName, snap.getKey());
      }

      // remove the map entry
      snapshotDBs.remove(dbName);
    }
    FSUtils.delTree(new File(getSnapshotDir(dbName, null)));

    // no delete log entries for the snapshots are needed here, since the
    // method will only be invoked when the database itself is deleted
  }
  /** Feed the transactionManager with the knowledge on how to handle snapshot related requests. */
  private void initializeTransactionManager() {

    dbs.getTransactionManager()
        .registerInMemoryProcessing(
            Operation.TYPE_CREATE_SNAP,
            new InMemoryProcessing() {

              @Override
              public Object[] deserializeRequest(ReusableBuffer serialized) throws BabuDBException {
                ObjectInputStream oin = null;
                try {
                  oin = new ObjectInputStream(new ByteArrayInputStream(serialized.array()));
                  int dbId = oin.readInt();
                  SnapshotConfig snap = (SnapshotConfig) oin.readObject();

                  return new Object[] {dbId, snap};
                } catch (Exception e) {
                  throw new BabuDBException(
                      ErrorCode.IO_ERROR,
                      "Could not deserialize operation of type "
                          + Operation.TYPE_CREATE_SNAP
                          + ", because: "
                          + e.getMessage(),
                      e);
                } finally {
                  try {
                    serialized.flip();
                    if (oin != null) oin.close();
                  } catch (IOException ioe) {
                    /* who cares? */
                  }
                }
              }

              @Override
              public OperationInternal convertToOperation(Object[] args) {
                return new BabuDBTransaction.BabuDBOperation(
                    Operation.TYPE_CREATE_SNAP, (String) null, new Object[] {args[0], args[1]});
              }

              @Override
              public Object process(OperationInternal operation) throws BabuDBException {

                Object[] args = operation.getParams();

                // parse args
                int dbId = (Integer) args[0];
                SnapshotConfig snap = (SnapshotConfig) args[1];

                // complete arguments
                if (dbId == InsertRecordGroup.DB_ID_UNKNOWN
                    && operation.getDatabaseName() != null) {
                  dbId =
                      dbs.getDatabaseManager()
                          .getDatabase(operation.getDatabaseName())
                          .getLSMDB()
                          .getDatabaseId();
                  operation.updateParams(new Object[] {dbId, snap});
                } else if (operation.getDatabaseName() == null) {
                  operation.updateDatabaseName(
                      dbs.getDatabaseManager().getDatabase(dbId).getName());
                }

                Map<String, Snapshot> snapMap = snapshotDBs.get(operation.getDatabaseName());
                if (snapMap == null) {
                  snapMap = new HashMap<String, Snapshot>();
                  snapshotDBs.put(operation.getDatabaseName(), snapMap);
                }

                // if the snapshot already exists ...
                if (snapMap.containsKey(snap.getName())) {

                  throw new BabuDBException(
                      ErrorCode.SNAP_EXISTS, "snapshot '" + snap.getName() + "' already exists");
                }

                snapMap.put(snap.getName(), new Snapshot(null, dbs));

                // first, create new in-memory snapshots of all indices
                int[] snapIds = null;
                try {
                  dbs.getTransactionManager().lockService();

                  // create the snapshot
                  snapIds =
                      dbs.getDatabaseManager()
                          .getDatabase(dbId)
                          .getLSMDB()
                          .createSnapshot(snap.getIndices());
                } catch (InterruptedException e) {
                  throw new BabuDBException(ErrorCode.INTERRUPTED, e.getMessage());
                } finally {
                  dbs.getTransactionManager().unlockService();
                }

                // then, enqueue a snapshot materialization request in the
                // checkpointer's queue
                dbs.getCheckpointer()
                    .addSnapshotMaterializationRequest(operation.getDatabaseName(), snapIds, snap);

                // as long as the snapshot has not been persisted yet, add a view on
                // the current snapshot in the original database to the snapshot DB map
                synchronized (snapshotDBs) {
                  Snapshot s = snapMap.get(snap.getName());
                  if (s.getView() == null) {
                    s.setView(new InMemoryView(dbs, operation.getDatabaseName(), snap, snapIds));
                  }
                }

                return null;
              }
            });

    dbs.getTransactionManager()
        .registerInMemoryProcessing(
            Operation.TYPE_DELETE_SNAP,
            new InMemoryProcessing() {

              @Override
              public Object[] deserializeRequest(ReusableBuffer serialized) throws BabuDBException {

                byte[] payload = serialized.array();
                int offs = payload[0];
                String dbName = new String(payload, 1, offs);
                String snapName = new String(payload, offs + 1, payload.length - offs - 1);
                serialized.flip();

                return new Object[] {dbName, snapName};
              }

              @Override
              public OperationInternal convertToOperation(Object[] args) {
                return new BabuDBTransaction.BabuDBOperation(
                    Operation.TYPE_DELETE_SNAP, (String) args[0], new Object[] {args[1]});
              }

              @Override
              public Object process(OperationInternal operation) throws BabuDBException {

                // parse args
                String snapshotName = (String) operation.getParams()[0];

                final Map<String, Snapshot> snapMap = snapshotDBs.get(operation.getDatabaseName());
                if (snapMap == null) {
                  throw new BabuDBException(
                      ErrorCode.NO_SUCH_SNAPSHOT, "snapshot '" + snapshotName + "' does not exist");
                }

                final Snapshot snap = snapMap.get(snapshotName);

                // if the snapshot does not exist ...
                if (snap == null) {
                  throw new BabuDBException(
                      ErrorCode.NO_SUCH_SNAPSHOT, "snapshot '" + snapshotName + "' does not exist");
                }

                // shut down and remove the view
                snap.getView().shutdown();
                snapMap.remove(snapshotName);

                // if a snapshot materialization request is currently in the
                // checkpointer queue, remove it
                dbs.getCheckpointer()
                    .removeSnapshotMaterializationRequest(
                        operation.getDatabaseName(), snapshotName);

                // delete the snapshot subdirectory on disk if available
                FSUtils.delTree(
                    new File(getSnapshotDir(operation.getDatabaseName(), snapshotName)));

                return null;
              }
            });
  }