Package com.sun.sgs.impl.service.data.store

Source Code of com.sun.sgs.impl.service.data.store.DbUtilities$Databases

/*
* Copyright 2007-2010 Sun Microsystems, Inc.
*
* This file is part of Project Darkstar Server.
*
* Project Darkstar Server is free software: you can redistribute it
* and/or modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation and
* distributed hereunder to you.
*
* Project Darkstar Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>.
*
* --
*/

package com.sun.sgs.impl.service.data.store;

import static com.sun.sgs.impl.service.data.store.
    DataStoreHeader.NEXT_NODE_ID_KEY;
import static com.sun.sgs.impl.service.data.store.
    DataStoreHeader.NEXT_OBJ_ID_KEY;
import com.sun.sgs.impl.sharedutil.LoggerWrapper;
import com.sun.sgs.service.store.db.DbCursor;
import com.sun.sgs.service.store.db.DbDatabase;
import com.sun.sgs.service.store.db.DbEnvironment;
import com.sun.sgs.service.store.db.DbTransaction;
import java.io.FileNotFoundException;
import java.io.ObjectStreamClass;
import java.security.DigestException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.logging.Level;

/**
* Utility methods for database operations needed to implement a {@code
* DataStore} using the {@link com.sun.sgs.service.store.db} package.  This
* class should not be instantiated.
*/
public final class DbUtilities {

    /** The number of bytes in a SHA-1 message digest. */
    private static final int SHA1_SIZE = 20;

    /** A message digest for use by the current thread. */
    private static final ThreadLocal<MessageDigest> messageDigest =
  new ThreadLocal<MessageDigest>() {
      protected MessageDigest initialValue() {
    try {
        return MessageDigest.getInstance("SHA-1");
    } catch (NoSuchAlgorithmException e) {
        throw new AssertionError(e);
    }
      }
        };

    /**
     * Stores information about the databases that constitute the data
     * store.
     */
    public static class Databases {
 
  /** The info database. */
  private DbDatabase info;

  /** The classes database. */
  private DbDatabase classes;

  /** The object IDs database. */
  private DbDatabase oids;

  /** The names database. */
  private DbDatabase names;

  /** Creates an instance of this class. */
  Databases() { }

  /**
   * Returns the info database.
   *
   * @return the info database
   */
  public DbDatabase info() {
      return info;
  }

  /**
   * Returns the classes database.
   *
   * @return the classes database
   */
  public DbDatabase classes() {
      return classes;
  }

  /**
   * Returns the object IDs database.
   *
   * @return the object IDs database
   */
  public DbDatabase oids() {
      return oids;
  }

  /**
   * Returns the names database.
   *
   * @return the names database
   */
  public DbDatabase names() {
      return names;
  }
    }

    /** This class should not be instantiated. */
    private DbUtilities() {
  throw new AssertionError();
    }

    /**
     * Opens or creates the Berkeley DB databases for a data store.
     *
     * @param  env the database environment
     * @param  dbTxn the database transaction
     * @param  logger the logger for logging messages
     * @return  the databases
     */
    public static Databases getDatabases(
  DbEnvironment env, DbTransaction dbTxn, LoggerWrapper logger)
    {
  Databases dbs = new Databases();
  boolean create = false;
  try {
      dbs.info = env.openDatabase(dbTxn, "info", false);
      int minorVersion = DataStoreHeader.verify(dbs.info, dbTxn);
      if (logger.isLoggable(Level.CONFIG)) {
    logger.log(Level.CONFIG, "Found existing header {0}",
         DataStoreHeader.headerString(minorVersion));
      }
  } catch (FileNotFoundException e) {
      try {
    dbs.info = env.openDatabase(dbTxn, "info", true);
      } catch (FileNotFoundException e2) {
    throw new DataStoreException(
        "Problem creating database: " + e2.getMessage(), e2);
      }
      DataStoreHeader.create(dbs.info, dbTxn);
      if (logger.isLoggable(Level.CONFIG)) {
    logger.log(Level.CONFIG, "Created new header {0}",
         DataStoreHeader.headerString());
      }
      create = true;
  }
  try {
      dbs.classes = env.openDatabase(dbTxn, "classes", create);
  } catch (FileNotFoundException e) {
      throw new DataStoreException(
    "Classes database not found: " + e.getMessage(), e);
  }
  try {
      dbs.oids = env.openDatabase(dbTxn, "oids", create);
  } catch (FileNotFoundException e) {
      throw new DataStoreException(
    "Oids database not found: " + e.getMessage(), e);
  }
  try {
      dbs.names = env.openDatabase(dbTxn, "names", create);
  } catch (FileNotFoundException e) {
      throw new DataStoreException(
    "Names database not found: " + e.getMessage(), e);
  }
  return dbs;
    }

    /**
     * Returns the next available object ID and increments the stored value by
     * the specified amount.
     *
     * @param  infoDb the info database
     * @param  dbTxn the transaction
     * @param  blockSize the number of items to allocate
     * @return  the next object ID
     */
    public static long getNextObjectId(
  DbDatabase infoDb, DbTransaction dbTxn, int blockSize)
    {
  return DataStoreHeader.getNextId(
      NEXT_OBJ_ID_KEY, infoDb, dbTxn, blockSize);
    }

    /**
     * Returns the next available node ID and increments the stored value by
     * the specified amount.
     *
     * @param  infoDb the info database
     * @param  dbTxn the transaction
     * @param  blockSize the number of node IDs to allocate
     * @return  the next node ID
     */
    public static long getNextNodeId(
  DbDatabase infoDb, DbTransaction dbTxn, int blockSize)
    {
  return DataStoreHeader.getNextId(
      NEXT_NODE_ID_KEY, infoDb, dbTxn, blockSize);
    }

    /**
     * Returns the class ID to represent classes with the specified class
     * information.  Obtains an existing ID for the class information if
     * present; otherwise, stores the information and returns the new ID
     * associated with it.  Class IDs are always greater than {@code 0}.  The
     * class information is the serialized form of the {@link
     * ObjectStreamClass} instance that serialization uses to represent the
     * class.
     *
     * @param  env the database environment
     * @param  classesDb the classes database
     * @param  classInfo the class information
     * @param  timeout the timeout to use when creating a transaction
     * @return  the associated class ID
     */
    public static int getClassId(DbEnvironment env,
         DbDatabase classesDb,
         byte[] classInfo,
         long timeout)
    {
  byte[] hashKey = getKeyFromClassInfo(classInfo);
  boolean done = false;
  /*
   * Use a separate transaction when obtaining the class ID so that
   * the ID will be available for other transactions to use right
   * away.  This approach means that the class info will be
   * registered even if the main transaction fails.  If any
   * transaction wants to register a new class, though, it's very
   * likely that the class will be needed, even if that transaction
   * aborts, so it makes sense to commit this operation separately to
   * improve concurrency.  -tjb@sun.com (05/23/2007)
   *
   * Use full transaction isolation to insure consistency when
   * concurrently allocating new class IDs.  -tjb@sun.com (03/17/2009)
   */
  DbTransaction dbTxn = env.beginTransaction(timeout, true);
  try {
      int result;
      byte[] hashValue = classesDb.get(dbTxn, hashKey, false);
      if (hashValue != null) {
    result = DataEncoding.decodeInt(hashValue);
      } else {
    DbCursor cursor = classesDb.openCursor(dbTxn);
    try {
        result = cursor.findLast()
      ? getClassIdFromKey(cursor.getKey()) + 1 : 1;
        byte[] idKey = getKeyFromClassId(result);
        boolean success =
      cursor.putNoOverwrite(idKey, classInfo);
        if (!success) {
      throw new DataStoreException(
          "Class ID key already present");
        }
    } finally {
        cursor.close();
    }
    boolean success = classesDb.putNoOverwrite(
        dbTxn, hashKey, DataEncoding.encodeInt(result));
    if (!success) {
        throw new DataStoreException(
      "Class hash already present");
    }
      }
      done = true;
      dbTxn.commit();
      return result;
  } finally {
      if (!done) {
    dbTxn.abort();
      }
  }
    }

    /**
     * Returns the class information associated with the specified class ID.
     * The class information is the serialized form of the {@link
     * ObjectStreamClass} instance that serialization uses to represent the
     * class.
     *
     * @param  env the database environment
     * @param  classesDb the classes database
     * @param  classId the class ID
     * @param  timeout the transaction timeout for creating a transaction
     * @return  the associated class information or {@code null} if the ID is
     *    not found
     * @throws  IllegalArgumentException if {@code classId} is not greater than
     *    {@code 0}
     */
    public static byte[] getClassInfo(
  DbEnvironment env, DbDatabase classesDb, int classId, long timeout)
    {
  if (classId <= 0) {
      throw new IllegalArgumentException(
    "The class ID must be greater than 0");
  }
  byte[] key = getKeyFromClassId(classId);
  boolean done = false;
  DbTransaction dbTxn = env.beginTransaction(timeout);
  try {
      byte[] result = classesDb.get(dbTxn, key, false);
      done = true;
      dbTxn.commit();
      return result;
  } finally {
      if (!done) {
    dbTxn.abort();
      }
  }
    }

    /** Converts a database key to a class ID. */
    private static int getClassIdFromKey(byte[] key) {
  assert key[0] == DataStoreHeader.CLASS_ID_PREFIX;
  return DataEncoding.decodeInt(key, 1);
    }

    /** Converts a class ID to a database key. */
    private static byte[] getKeyFromClassId(int classId) {
  byte[] key = new byte[5];
  key[0] = DataStoreHeader.CLASS_ID_PREFIX;
  DataEncoding.encodeInt(classId, key, 1);
  return key;
    }

    /** Converts class information to a database key. */
    private static byte[] getKeyFromClassInfo(byte[] classInfo) {
  byte[] keyBytes = new byte[1 + SHA1_SIZE];
  keyBytes[0] = DataStoreHeader.CLASS_HASH_PREFIX;
  MessageDigest md = messageDigest.get();
  try {
      md.update(classInfo);
      int numBytes = md.digest(keyBytes, 1, SHA1_SIZE);
      assert numBytes == SHA1_SIZE;
      return keyBytes;
  } catch (DigestException e) {
      throw new AssertionError(e);
  }
    }
}
TOP

Related Classes of com.sun.sgs.impl.service.data.store.DbUtilities$Databases

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.