Package fr.dyade.aaa.util

Source Code of fr.dyade.aaa.util.DBOperation

/*
* Copyright (C) 2006 - 2010 ScalAgent Distributed Technologies
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
* USA.
*
* Initial developer(s): ScalAgent Distributed Technologies
* Contributor(s):
*/
package fr.dyade.aaa.util;

import java.io.IOException;
import java.io.Serializable;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;

import org.objectweb.util.monolog.api.BasicLevel;

import fr.dyade.aaa.common.Pool;

/**
*  The DBTransaction class implements a transactionnal storage through
* a JDBC interface. This class is designed to be specialized for different
* database implementation.
* <p>
* Be Careful, the configuration properties don't work for the transaction component:
* these properties are saved in the transaction repository so they can not be used to
* configure it.
*
* @see Transaction
* @see MySQLDBTransaction
* @see DerbyDBTransaction
*/
public abstract class DBTransaction extends AbstractTransaction implements DBTransactionMBean {
  /**
   *  Number of pooled operation, by default 1000.
   *  This value can be adjusted for a particular server by setting
   * <code>DBLogThresholdOperation</code> specific property.
   * <p>
   *  This property can be fixed only from <code>java</code> launching
   * command, or through System.property method.
   */
  static int LogThresholdOperation = 1000;

  /**
   * Returns the pool size for <code>operation</code> objects, by default 1000.
   *
   * @return The pool size for <code>operation</code> objects.
   */
  public int getLogThresholdOperation() {
    return LogThresholdOperation;
  }

  protected Connection conn = null;

  private PreparedStatement insertStmt = null;
  private PreparedStatement updateStmt = null;
  private PreparedStatement deleteStmt = null;

  public DBTransaction() {}

  public void initRepository() throws IOException {
    initDB();

    try {
      insertStmt = conn.prepareStatement("INSERT INTO JoramDB VALUES (?, ?)");
      updateStmt = conn.prepareStatement("UPDATE JoramDB SET content=? WHERE name=?");
      deleteStmt = conn.prepareStatement("DELETE FROM JoramDB WHERE name=?");
    } catch (SQLException sqle) {
      sqle.printStackTrace();
      throw new IOException(sqle.getMessage());
    }
  }

  /**
   * Instantiates the database driver and creates the table if necessary
   * @throws IOException
   */
  protected abstract void initDB() throws IOException;

  /**
   * Returns the path of persistence directory.
   *
   * @return The path of persistence directory.
   */
  public String getPersistenceDir() {
    return dir.getPath();
  }

  protected final void setPhase(int newPhase) {
    phase = newPhase;
  }

  /**
   *  Returns an array of strings naming the persistent objects denoted by
   * a name that satisfy the specified prefix. Each string is an object name.
   *
   * @param prefix  the prefix
   * @return    An array of strings naming the persistent objects
   *     denoted by a name that satisfy the specified prefix. The
   *     array will be empty if no names match.
   */
  public final synchronized String[] getList(String prefix) {
    try {
      // Creating a statement lets us issue commands against the connection.
      Statement s = conn.createStatement();
      ResultSet rs = s.executeQuery("SELECT name FROM JoramDB WHERE name LIKE '" + prefix + "%'");

      Vector v = new Vector();
      while (rs.next()) {
        v.add(rs.getString(1));
      }
      rs.close();
      s.close();

      String[] result = new String[v.size()];
      result = (String[]) v.toArray(result);

      if (logmon.isLoggable(BasicLevel.DEBUG))
        logmon.log(BasicLevel.DEBUG, "DBTransaction, getList: " + v);

      return result;
    } catch (SQLException sqle) {
      // TODO: AF
    }
    return null;
  }

  /**
   * Tests if the Transaction component is persistent.
   *
   * @return true.
   */
  public boolean isPersistent() {
    return true;
  }

  final String fname(String dirName, String name) {
    if (dirName == null) return name;
    return new StringBuffer(dirName).append('/').append(name).toString();
  }

  protected final void saveInLog(byte[] buf,
                                 String dirName, String name,
                                 Hashtable log,
                                 boolean copy,
                                 boolean first) throws IOException {
    String fname = fname(dirName, name);
   
    if (logmon.isLoggable(BasicLevel.DEBUG))
      logmon.log(BasicLevel.DEBUG,
                 "DBTransaction, saveInLog(" + fname + ", " + copy + ", " + first + ")");

    DBOperation op = DBOperation.alloc(DBOperation.SAVE, fname, buf);
    DBOperation old = (DBOperation) log.put(fname, op);
    if (copy) {
      if ((old != null) &&
          (old.type == DBOperation.SAVE) &&
          (old.value.length == buf.length)) {
        // reuse old buffer
        op.value = old.value;
      } else {
        // alloc a new one
        op.value = new byte[buf.length];
      }
      System.arraycopy(buf, 0, op.value, 0, buf.length);
    }
    if (old != null) old.free();
  }

  public byte[] loadByteArray(String dirName, String name) throws IOException {
    String fname = fname(dirName, name);

    if (logmon.isLoggable(BasicLevel.DEBUG))
      logmon.log(BasicLevel.DEBUG, "DBTransaction, loadByteArray(" + fname + ")");

    // Searchs in the log a new value for the object.
    Hashtable log = ((Context) perThreadContext.get()).getLog();
    DBOperation op = (DBOperation) log.get(fname);
    if (op != null) {
      if (op.type == DBOperation.SAVE) {
        return op.value;
      } else if (op.type == DBOperation.DELETE) {
        // The object was deleted.
        return null;
      }
    }

    try {
      // Creating a statement lets us issue commands against the connection.
      Statement s = conn.createStatement();
      //
      ResultSet rs = s.executeQuery("SELECT content FROM JoramDB WHERE name='" + name + "'");

      if (!rs.next()) return null;

      byte[] content = rs.getBytes(1);

      rs.close();
      s.close();

      return content;
    } catch (SQLException sqle) {
      throw new IOException(sqle.getMessage());
    }
  }

  public void delete(String dirName, String name) {
    String fname = fname(dirName, name);

    if (logmon.isLoggable(BasicLevel.DEBUG))
      logmon.log(BasicLevel.DEBUG,
                 "DBTransaction, delete(" + fname + ")");

    Hashtable log = ((Context) perThreadContext.get()).getLog();
    DBOperation op = DBOperation.alloc(DBOperation.DELETE, fname);
    op = (DBOperation) log.put(fname, op);
    if (op != null) op.free();
  }

  public final synchronized void commit(boolean release) throws IOException {
    if (phase != RUN)
      throw new IllegalStateException("Can not commit.");

    if (logmon.isLoggable(BasicLevel.DEBUG))
      logmon.log(BasicLevel.DEBUG, "DBTransaction, commit");
   
    Hashtable log = ((Context) perThreadContext.get()).getLog();
    if (! log.isEmpty()) {
      DBOperation op = null;
      for (Enumeration e = log.elements(); e.hasMoreElements(); ) {
        op = (DBOperation) e.nextElement();
        if (op.type == DBOperation.SAVE) {
          if (logmon.isLoggable(BasicLevel.DEBUG))
            logmon.log(BasicLevel.DEBUG,
                       "DBTransaction, commit.save (" + op.name + ", " + op.value.length + ')');

          try {
            insertStmt.setString(1, op.name);
            insertStmt.setBytes(2, op.value);
            insertStmt.executeUpdate();
          } catch (SQLException sqle1) {
            try {
              updateStmt.setBytes(1, op.value);
              updateStmt.setString(2, op.name);
              updateStmt.executeUpdate();
            } catch (SQLException sqle) {
              throw new IOException(sqle.getMessage());
            }
          }
        } else if (op.type == DBOperation.DELETE) {
          if (logmon.isLoggable(BasicLevel.DEBUG))
            logmon.log(BasicLevel.DEBUG,
                       "DBTransaction, commit.delete (" + op.name + ')');

          try {
            deleteStmt.setString(1, op.name);
            deleteStmt.executeUpdate();
          } catch (SQLException sqle) {
            throw new IOException(sqle.getMessage());
          }
        }
        op.free();
      }
      log.clear();

      try {
        conn.commit();
      } catch (SQLException sqle) {
        throw new IOException(sqle.getMessage());
      }
    }

    if (logmon.isLoggable(BasicLevel.DEBUG))
      logmon.log(BasicLevel.DEBUG, "DBTransaction, committed");
   
    if (release) {
      // Change the transaction state and save it.
      setPhase(FREE);
      notify();
    } else {
      setPhase(COMMIT);
    }
  }

  /**
   * Stops the transaction module.
   * It waits all transactions termination, then the module is kept
   * in a FREE 'ready to use' state.
   */
  public synchronized void stop() {
    if (logmon.isLoggable(BasicLevel.INFO))
      logmon.log(BasicLevel.INFO, "DBTransaction, stops");

    while (phase != FREE) {
      // Wait for the transaction subsystem to be free
      try {
        wait();
      } catch (InterruptedException exc) {
      }
    }
    setPhase(FINALIZE);

//     server.shutdown();
    try {
//       org.hsqldb.DatabaseManager.closeDatabases(0);
      // Creating a statement lets us issue commands against the connection.
      Statement s = conn.createStatement();
      // .
//       s.execute("CHECKPOINT DEFRAG");
      conn.commit();
    logmon.log(BasicLevel.INFO, "DBTransaction, TBR stop#3");
      s.executeUpdate("SHUTDOWN COMPACT");
    logmon.log(BasicLevel.INFO, "DBTransaction, TBR stop#4");
      s.close();
    logmon.log(BasicLevel.INFO, "DBTransaction, TBR stop#5");
    } catch (SQLException sqle) {
// AF: TODO
//       throw new IOException(sqle.getMessage());
      logmon.log(BasicLevel.ERROR, "DBTransaction, stop#6", sqle);
    } catch (Throwable t) {
      logmon.log(BasicLevel.ERROR, "DBTransaction, stop#7", t);
    } finally {
      logmon.log(BasicLevel.INFO, "DBTransaction, stop#8");
    }
    logmon.log(BasicLevel.INFO, "DBTransaction, TBR stop#9");
    setPhase(FREE);

    if (logmon.isLoggable(BasicLevel.INFO)) {
      logmon.log(BasicLevel.INFO, "NTransaction, stopped: ");
    }
  }

  /**
   * Close the transaction module.
   * It waits all transactions termination, the module will be initialized
   * anew before reusing it.
   */
  public synchronized void close() {
    if (logmon.isLoggable(BasicLevel.INFO))
      logmon.log(BasicLevel.INFO, "DBTransaction, close");

    if (phase == INIT) return;

    while (phase != FREE) {
      // Wait for the transaction subsystem to be free
      try {
        wait();
      } catch (InterruptedException exc) {
      }
    }

    setPhase(FINALIZE);
    try {
      // Creating a statement lets us issue commands against the connection.
      Statement s = conn.createStatement();
      // .
      s.execute("SHUTDOWN COMPACT");
      s.close();
    } catch (SQLException sqle) {
// AF: TODO
//       throw new IOException(sqle.getMessage());
      logmon.log(BasicLevel.ERROR, "DBTransaction, close", sqle);
    }
    setPhase(INIT);

    if (logmon.isLoggable(BasicLevel.INFO)) {
      logmon.log(BasicLevel.INFO, "DBTransaction, closed: ");
    }
  }
}

final class DBOperation implements Serializable {
  /**
   *
   */
  private static final long serialVersionUID = 1L;
  static final int SAVE = 1;
  static final int DELETE = 2;
  static final int COMMIT = 3;
  static final int END = 127;

  int type;
  String name;
  byte[] value;

  private DBOperation(int type, String name, byte[] value) {
    this.type = type;
    this.name = name;
    this.value = value;
  }

  /**
   * Returns a string representation for this object.
   *
   * @return  A string representation of this object.
   */
  public String toString() {
    StringBuffer strbuf = new StringBuffer();

    strbuf.append('(').append(super.toString());
    strbuf.append(",type=").append(type);
    strbuf.append(",name=").append(name);
    strbuf.append(')');

    return strbuf.toString();
  }

  private static Pool pool = null;

  static {
    pool = new Pool("DBTransaction$Operation",
                    Integer.getInteger("DBLogThresholdOperation",
                                       DBTransaction.LogThresholdOperation).intValue());
  }

  static DBOperation alloc(int type, String name) {
    return alloc(type, name, null);
  }

  static DBOperation alloc(int type, String name, byte[] value) {
    DBOperation op = null;

    try {
      op = (DBOperation) pool.allocElement();
    } catch (Exception exc) {
      return new DBOperation(type, name, value);
    }
    op.type = type;
    op.name = name;
    op.value = value;

    return op;
  }

  void free() {
    /* to let gc do its work */
    name = null;
    value = null;
    pool.freeElement(this);
  }
}
TOP

Related Classes of fr.dyade.aaa.util.DBOperation

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.