Main Page | Packages | Class Hierarchy | Alphabetical List | Class List | File List | Class Members | Related Pages

JDBCRecoveryLog.java

00001 /**
00002  * C-JDBC: Clustered JDBC.
00003  * Copyright (C) 2002-2005 French National Institute For Research In Computer
00004  * Science And Control (INRIA).
00005  * Contact: c-jdbc@objectweb.org
00006  * 
00007  * This library is free software; you can redistribute it and/or modify it
00008  * under the terms of the GNU Lesser General Public License as published by the
00009  * Free Software Foundation; either version 2.1 of the License, or any later
00010  * version.
00011  * 
00012  * This library is distributed in the hope that it will be useful, but WITHOUT
00013  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00014  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
00015  * for more details.
00016  * 
00017  * You should have received a copy of the GNU Lesser General Public License
00018  * along with this library; if not, write to the Free Software Foundation,
00019  * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
00020  *
00021  * Initial developer(s): Emmanuel Cecchet.
00022  * Contributor(s): Julie Marguerite, Greg Ward, Jess Sightler.
00023  */
00024 
00025 package org.objectweb.cjdbc.controller.recoverylog;
00026 
00027 import java.sql.Connection;
00028 import java.sql.DatabaseMetaData;
00029 import java.sql.PreparedStatement;
00030 import java.sql.ResultSet;
00031 import java.sql.SQLException;
00032 import java.sql.Statement;
00033 import java.util.ArrayList;
00034 
00035 import javax.management.NotCompliantMBeanException;
00036 
00037 import org.objectweb.cjdbc.common.i18n.Translate;
00038 import org.objectweb.cjdbc.common.jmx.mbeans.AbstractRecoveryLogMBean;
00039 import org.objectweb.cjdbc.common.shared.BackendState;
00040 import org.objectweb.cjdbc.common.sql.AbstractRequest;
00041 import org.objectweb.cjdbc.common.sql.AbstractWriteRequest;
00042 import org.objectweb.cjdbc.common.sql.AlterRequest;
00043 import org.objectweb.cjdbc.common.sql.CreateRequest;
00044 import org.objectweb.cjdbc.common.sql.DeleteRequest;
00045 import org.objectweb.cjdbc.common.sql.DropRequest;
00046 import org.objectweb.cjdbc.common.sql.InsertRequest;
00047 import org.objectweb.cjdbc.common.sql.StoredProcedure;
00048 import org.objectweb.cjdbc.common.sql.UpdateRequest;
00049 import org.objectweb.cjdbc.common.xml.DatabasesXmlTags;
00050 import org.objectweb.cjdbc.controller.connection.DriverManager;
00051 import org.objectweb.cjdbc.controller.loadbalancer.tasks.BeginTask;
00052 import org.objectweb.cjdbc.controller.loadbalancer.tasks.CommitTask;
00053 import org.objectweb.cjdbc.controller.loadbalancer.tasks.ReadStoredProcedureTask;
00054 import org.objectweb.cjdbc.controller.loadbalancer.tasks.RollbackTask;
00055 import org.objectweb.cjdbc.controller.loadbalancer.tasks.WriteRequestTask;
00056 import org.objectweb.cjdbc.controller.loadbalancer.tasks.WriteStoredProcedureTask;
00057 import org.objectweb.cjdbc.controller.requestmanager.TransactionMarkerMetaData;
00058 
00059 /**
00060  * Recovery Log using a database accessed through JDBC.
00061  * 
00062  * @author <a href="mailto:Emmanuel.Cecchet@inria.fr">Emmanuel Cecchet </a>
00063  * @author <a href="mailto:Julie.Marguerite@inria.fr">Julie Marguerite </a>
00064  * @author <a href="mailto:Nicolas.Modrzyk@inrialpes.fr">Nicolas Modrzyk </a>*
00065  * @version 1.0
00066  */
00067 public class JDBCRecoveryLog extends AbstractRecoveryLog
00068     implements
00069       AbstractRecoveryLogMBean
00070 {
00071   /** Driver class name. */
00072   private String            driverClassName;
00073 
00074   /** Driver name. */
00075   private String            driverName;
00076 
00077   /** Driver URL. */
00078   private String            url;
00079 
00080   /** User's login. */
00081   private String            login;
00082 
00083   /** User's password. */
00084   private String            password;
00085 
00086   /** Name of the log table. */
00087   private String            logTableName;
00088   private String            logTableCreateStatement;
00089   // keep record of values
00090   private String            idType;
00091   private String            vloginType;
00092   private String            sqlType;
00093   private String            transactionIdType;
00094   private String            logextraStatement;
00095 
00096   /** Checkpoint table name. */
00097   private String            checkpointTableName;
00098   private String            checkpointTableCreateStatement;
00099 
00100   /**
00101    * Backend table
00102    * 
00103    * @see #setBackendTableCreateStatement(String, String, String, String,
00104    *      String, String)
00105    */
00106   private String            backendTableCreateStatement;
00107   private String            backendTableName;
00108 
00109   // Keep record of the values
00110   private String            nameType;
00111   private String            requestIdType;
00112   private String            checkextraStatement;
00113 
00114   /** Connection to the database. */
00115   private Connection        connection;
00116 
00117   /** <code>PreparedStatement</code> used to log requests. */
00118   private PreparedStatement pstmt;
00119 
00120   /** Current maximum value of the primary key in logTableName. */
00121   private long              logTableId = 0;
00122 
00123   /** Timeout for SQL requests. */
00124   private int               timeout;
00125 
00126   private JDBCLoggerThread  loggerThread;
00127 
00128   private String            sqlColumnName;
00129 
00130   /**
00131    * Creates a new <code>JDBCRecoveryLog</code> instance.
00132    * 
00133    * @param driverName the driverClassName name.
00134    * @param driverClassName the driverClassName class name.
00135    * @param url the JDBC URL.
00136    * @param login the login to use to connect to the database.
00137    * @param password the password to connect to the database.
00138    * @param requestTimeout timeout in seconds for update queries.
00139    * @throws NotCompliantMBeanException if the MBean is not JMX compliant
00140    */
00141   public JDBCRecoveryLog(String driverName, String driverClassName, String url,
00142       String login, String password, int requestTimeout)
00143       throws NotCompliantMBeanException
00144   {
00145     super(AbstractRecoveryLogMBean.class);
00146     this.driverName = driverName;
00147     this.driverClassName = driverClassName;
00148     this.url = url;
00149     this.login = login;
00150     this.password = password;
00151     this.timeout = requestTimeout;
00152 
00153     // Connect to the database
00154     try
00155     {
00156       connectToDatabase();
00157     }
00158     catch (SQLException e)
00159     {
00160       throw new RuntimeException("Unable to connect to the database: " + e);
00161     }
00162 
00163     // Logger thread will be created in checkRecoveryLogTables()
00164     // after database has been initialized
00165   }
00166 
00167   /**
00168    * Checks if the tables (log and checkpoint) already exist, and create them if
00169    * needed.
00170    */
00171   private void intializeDatabase() throws SQLException
00172   {
00173     boolean createLogTable = true;
00174     boolean createCheckpointTable = true;
00175     boolean createBackendTable = true;
00176     // Check if tables exist
00177     try
00178     {
00179       connection.setAutoCommit(false);
00180       // Get DatabaseMetaData
00181       DatabaseMetaData metaData = connection.getMetaData();
00182 
00183       // Get a description of tables matching the catalog, schema, table name
00184       // and type.
00185       // Sending in null for catalog and schema drop them from the selection
00186       // criteria. Replace the last argument in the getTables method with
00187       // types below to obtain only database tables. (Sending in null
00188       // retrieves all types).
00189       String[] types = {"TABLE", "VIEW"};
00190       ResultSet rs = metaData.getTables(null, null, "%", types);
00191 
00192       // Get tables metadata
00193       String tableName;
00194       while (rs.next())
00195       {
00196         // 1 is table catalog, 2 is table schema, 3 is table name, 4 is type
00197         tableName = rs.getString(3);
00198         if (logger.isDebugEnabled())
00199           logger.debug(Translate.get("recovery.jdbc.table.found", tableName));
00200         if (tableName.equalsIgnoreCase(logTableName))
00201         {
00202           if (tableName.compareTo(logTableName) != 0)
00203             logger.warn(Translate.get("recovery.jdbc.logtable.case.mismatch",
00204                 new String[]{logTableName, tableName}));
00205           createLogTable = false;
00206           // initialize logTableId
00207           PreparedStatement p = null;
00208           try
00209           {
00210             ResultSet result = null;
00211             p = connection.prepareStatement("SELECT MAX(id) AS max_id FROM "
00212                 + logTableName);
00213             result = p.executeQuery();
00214             if (result.next())
00215               logTableId = result.getInt("max_id");
00216             else
00217               logTableId = 0;
00218             p.close();
00219           }
00220           catch (SQLException e)
00221           {
00222             try
00223             {
00224               if (p != null)
00225                 p.close();
00226             }
00227             catch (Exception ignore)
00228             {
00229             }
00230             throw new RuntimeException(Translate.get(
00231                 "recovery.jdbc.logtable.getvalue.failed", e));
00232           }
00233 
00234         }
00235         if (tableName.equalsIgnoreCase(checkpointTableName))
00236         {
00237           if (tableName.compareTo(checkpointTableName) != 0)
00238             logger.warn(Translate.get(
00239                 "recovery.jdbc.checkpointtable.case.mismatch", new String[]{
00240                     checkpointTableName, tableName}));
00241           createCheckpointTable = false;
00242         }
00243         if (tableName.equalsIgnoreCase(backendTableName))
00244         {
00245           if (tableName.compareTo(backendTableName) != 0)
00246             logger.warn(Translate.get(
00247                 "recovery.jdbc.backendtable.case.mismatch", new String[]{
00248                     backendTableName, tableName}));
00249           createBackendTable = false;
00250         }
00251       }
00252       try
00253       {
00254         connection.commit();
00255         connection.setAutoCommit(true);
00256       }
00257       catch (Exception ignore)
00258       {
00259         // Read-only transaction we don't care
00260       }
00261     }
00262     catch (SQLException e)
00263     {
00264       logger.error(Translate.get("recovery.jdbc.table.no.description"), e);
00265       throw e;
00266     }
00267 
00268     // Create the missing tables
00269     Statement stmt = null;
00270     if (createLogTable)
00271     {
00272       if (logger.isInfoEnabled())
00273         logger.info(Translate
00274             .get("recovery.jdbc.logtable.create", logTableName));
00275       try
00276       {
00277         stmt = connection.createStatement();
00278         stmt.executeUpdate(logTableCreateStatement);
00279         stmt.close();
00280       }
00281       catch (SQLException e)
00282       {
00283         throw new SQLException(Translate.get(
00284             "recovery.jdbc.logtable.create.failed", new String[]{logTableName,
00285                 e.getMessage()}));
00286       }
00287     }
00288     if (createCheckpointTable)
00289     {
00290       if (logger.isInfoEnabled())
00291         logger.info(Translate.get("recovery.jdbc.checkpointtable.create",
00292             checkpointTableName));
00293       try
00294       {
00295         stmt = connection.createStatement();
00296         stmt.executeUpdate(checkpointTableCreateStatement);
00297         stmt.close();
00298       }
00299       catch (SQLException e)
00300       {
00301         throw new SQLException(Translate.get(
00302             "recovery.jdbc.checkpointtable.create.failed", new String[]{
00303                 logTableName, e.getMessage()}));
00304       }
00305 
00306       // Add an initial checkpoint in the table
00307       String checkpointName = "Initial_empty_recovery_log";
00308       try
00309       {
00310         if (logger.isDebugEnabled())
00311           logger.debug("Storing checkpoint " + checkpointName
00312               + " at request id " + logTableId);
00313         pstmt = connection.prepareStatement("INSERT INTO "
00314             + checkpointTableName + " VALUES(?,?)");
00315         pstmt.setString(1, checkpointName);
00316         pstmt.setLong(2, logTableId);
00317         pstmt.executeUpdate();
00318         pstmt.close();
00319       }
00320       catch (SQLException e)
00321       {
00322         try
00323         {
00324           if (pstmt != null)
00325             pstmt.close();
00326         }
00327         catch (Exception ignore)
00328         {
00329         }
00330         throw new SQLException(Translate.get(
00331             "recovery.jdbc.checkpoint.store.failed", new String[]{
00332                 checkpointName, e.getMessage()}));
00333       }
00334     }
00335     if (createBackendTable)
00336     {
00337       if (logger.isInfoEnabled())
00338         logger.info(Translate.get("recovery.jdbc.backendtable.create",
00339             backendTableName));
00340       try
00341       {
00342         stmt = connection.createStatement();
00343         stmt.executeUpdate(backendTableCreateStatement);
00344         stmt.close();
00345       }
00346       catch (SQLException e)
00347       {
00348         throw new SQLException(Translate.get(
00349             "recovery.jdbc.backendtable.create.failed", new String[]{
00350                 logTableName, e.getMessage()}));
00351       }
00352     }
00353 
00354   }
00355 
00356   /**
00357    * Gets a connection to the database.
00358    * 
00359    * @exception SQLException if an error occurs.
00360    */
00361   private void connectToDatabase() throws SQLException
00362   {
00363     // Get a connection
00364     try
00365     {
00366       if (logger.isDebugEnabled())
00367         logger.debug(Translate.get("recovery.jdbc.connect", new String[]{url,
00368             login}));
00369       connection = DriverManager.getConnection(url, login, password,
00370           driverName, driverClassName);
00371     }
00372     catch (RuntimeException e)
00373     {
00374       String msg = Translate.get("recovery.jdbc.connect.failed", e);
00375       logger.debug(msg, e);
00376       throw new SQLException(msg);
00377     }
00378     catch (SQLException e)
00379     {
00380       String msg = Translate.get("recovery.jdbc.connect.failed", e);
00381       logger.debug(msg, e);
00382       throw new SQLException(msg);
00383     }
00384   }
00385 
00386   //
00387   //
00388   // Logging related methods
00389   //
00390   //
00391 
00392   /**
00393    * @see org.objectweb.cjdbc.controller.recoverylog.AbstractRecoveryLog#logRequest(AbstractWriteRequest)
00394    */
00395   public void logRequest(AbstractWriteRequest request) throws SQLException
00396   {
00397     loggerThread.log(incrementLogTableId(), request.getLogin(), request
00398         .getSQL(), request.getTransactionId(), request.getEscapeProcessing());
00399   }
00400 
00401   /**
00402    * @see org.objectweb.cjdbc.controller.recoverylog.AbstractRecoveryLog#logRequest(org.objectweb.cjdbc.common.sql.StoredProcedure,boolean)
00403    */
00404   public void logRequest(StoredProcedure proc, boolean isRead)
00405       throws SQLException
00406   {
00407     if (isRead)
00408       loggerThread.log(incrementLogTableId(), proc.getLogin(), proc.getSQL(),
00409           proc.getTransactionId(), proc.getEscapeProcessing());
00410     else
00411     { // Reverse the first bracket so that we can identify a write call
00412       StringBuffer writeCall = new StringBuffer(proc.getSQL());
00413       writeCall.setCharAt(0, '}');
00414       loggerThread.log(incrementLogTableId(), proc.getLogin(), writeCall
00415           .toString(), proc.getTransactionId(), proc.getEscapeProcessing());
00416     }
00417   }
00418 
00419   /**
00420    * @see org.objectweb.cjdbc.controller.recoverylog.AbstractRecoveryLog#getLastTransactionId()
00421    */
00422   public long getLastTransactionId() throws SQLException
00423   {
00424     Statement stmt = null;
00425     ResultSet rs = null;
00426     try
00427     {
00428       stmt = connection.createStatement();
00429       rs = stmt.executeQuery("select max(transaction_id) from " + logTableName);
00430       if (rs.next())
00431         return rs.getInt(1);
00432       else
00433         return 0;
00434     }
00435     catch (SQLException e)
00436     {
00437       throw e;
00438     }
00439     finally
00440     {
00441       try
00442       {
00443         if (rs != null)
00444           rs.close();
00445       }
00446       catch (Exception ignore)
00447       {
00448       }
00449       try
00450       {
00451         if (stmt != null)
00452           stmt.close();
00453       }
00454       catch (Exception ignore)
00455       {
00456       }
00457     }
00458   }
00459 
00460   /**
00461    * Increments the value of logTableId.
00462    */
00463   private synchronized long incrementLogTableId()
00464   {
00465     logTableId++;
00466     return logTableId;
00467   }
00468 
00469   /**
00470    * @see org.objectweb.cjdbc.controller.recoverylog.AbstractRecoveryLog#begin(TransactionMarkerMetaData)
00471    */
00472   public void begin(TransactionMarkerMetaData tm) throws SQLException
00473   {
00474     // Store the begin in the database
00475     loggerThread.log(incrementLogTableId(), tm.getLogin(), "begin", tm
00476         .getTransactionId(), false);
00477   }
00478 
00479   /**
00480    * @see org.objectweb.cjdbc.controller.recoverylog.AbstractRecoveryLog#abort(org.objectweb.cjdbc.controller.requestmanager.TransactionMarkerMetaData)
00481    */
00482   public void abort(TransactionMarkerMetaData tm) throws SQLException
00483   {
00484     // We have to perform exactly the same job as a rollback
00485     rollback(tm);
00486   }
00487 
00488   /**
00489    * @see org.objectweb.cjdbc.controller.recoverylog.AbstractRecoveryLog#commit(TransactionMarkerMetaData)
00490    */
00491   public void commit(TransactionMarkerMetaData tm) throws SQLException
00492   {
00493     // Some backends started a recovery process, log the commit
00494     loggerThread.log(incrementLogTableId(), tm.getLogin(), "commit", tm
00495         .getTransactionId(), false);
00496   }
00497 
00498   /**
00499    * @see org.objectweb.cjdbc.controller.recoverylog.AbstractRecoveryLog#rollback(TransactionMarkerMetaData)
00500    */
00501   public void rollback(TransactionMarkerMetaData tm) throws SQLException
00502   {
00503     long transactionId = tm.getTransactionId();
00504     if (isRecovering())
00505     {
00506       // Some backends started a recovery process, log the rollback
00507       loggerThread.log(incrementLogTableId(), tm.getLogin(), "rollback",
00508           transactionId, false);
00509     }
00510     else
00511     {
00512       // The transaction failed
00513       // Remove the requests with this transactionId from the database
00514       loggerThread.removeQueriesOfTransactionFromQueue(transactionId);
00515       PreparedStatement stmt = null;
00516       try
00517       {
00518         stmt = connection.prepareStatement("DELETE FROM " + logTableName
00519             + " WHERE transaction_id=?");
00520         stmt.setLong(1, transactionId);
00521         stmt.executeUpdate();
00522       }
00523       catch (SQLException e)
00524       {
00525         throw new SQLException(Translate.get(
00526             "recovery.jdbc.transaction.remove.failed", new String[]{
00527                 String.valueOf(transactionId), e.getMessage()}));
00528       }
00529       finally
00530       {
00531         try
00532         {
00533           if (stmt != null)
00534             stmt.close();
00535         }
00536         catch (Exception ignore)
00537         {
00538         }
00539       }
00540     }
00541   }
00542 
00543   //
00544   //
00545   // Recovery related functions
00546   //
00547   //
00548 
00549   /**
00550    * @see org.objectweb.cjdbc.controller.recoverylog.AbstractRecoveryLog#getCheckpointRequestId(String)
00551    */
00552   public long getCheckpointRequestId(String checkpointName) throws SQLException
00553   {
00554     long requestId = -1;
00555     PreparedStatement stmt = null;
00556     ResultSet rs = null;
00557     try
00558     {
00559       stmt = connection.prepareStatement("SELECT request_id FROM "
00560           + checkpointTableName + " WHERE name LIKE ?");
00561       stmt.setString(1, checkpointName);
00562       rs = stmt.executeQuery();
00563 
00564       if (rs.next())
00565         requestId = rs.getInt("request_id");
00566       else
00567       {
00568         String msg = Translate.get("recovery.jdbc.checkpoint.not.found",
00569             checkpointName);
00570         logger.info(msg);
00571         throw new SQLException(msg);
00572       }
00573     }
00574     catch (SQLException e)
00575     {
00576       throw new SQLException(Translate.get(
00577           "recovery.jdbc.checkpoint.not.found.error", new String[]{
00578               checkpointName, e.getMessage()}));
00579     }
00580     finally
00581     {
00582       try
00583       {
00584         if (rs != null)
00585           rs.close();
00586       }
00587       catch (Exception ignore)
00588       {
00589       }
00590       try
00591       {
00592         if (stmt != null)
00593           stmt.close();
00594       }
00595       catch (Exception ignore)
00596       {
00597       }
00598     }
00599     return requestId;
00600   }
00601 
00602   /**
00603    * @see org.objectweb.cjdbc.controller.recoverylog.AbstractRecoveryLog#recoverNextRequest(long)
00604    */
00605   public RecoveryTask recoverNextRequest(long previousRequestId)
00606       throws SQLException
00607   {
00608     RecoveryTask task = null;
00609 
00610     // Get the request with the id after previousRequestId.
00611     PreparedStatement stmt = null;
00612     try
00613     {
00614       ResultSet rs = null;
00615       boolean emptyResult;
00616 
00617       stmt = connection.prepareStatement("SELECT * FROM " + logTableName
00618           + " WHERE id>? AND id<=? ORDER BY id");
00619       // Note that the statement is closed in the finally block
00620       do
00621       {
00622         // Take a window of 2 requests if there is a small hole
00623         // induced by a removed begin/commit/rollback command.
00624         stmt.setLong(1, previousRequestId);
00625         stmt.setLong(2, previousRequestId + 2);
00626         // Close ResultSet of previous loop step to free resources.
00627         if (rs != null)
00628           rs.close();
00629         rs = stmt.executeQuery();
00630         previousRequestId += 2; // Shift the window
00631         emptyResult = !rs.next();
00632       }
00633       while (emptyResult && (previousRequestId <= logTableId));
00634 
00635       // No more request after this one
00636       if (emptyResult)
00637       {
00638         rs.close();
00639         return null;
00640       }
00641 
00642       String sql = rs.getString(sqlColumnName);
00643       String user = rs.getString("vlogin");
00644       int transactionId = rs.getInt("transaction_id");
00645       int id = rs.getInt("id");
00646       rs.close();
00647 
00648       // Construct the request object according to its type
00649       boolean escapeProcessing = true;
00650       sql = sql.trim();
00651       // Check that the command starts with (only 2 letters are needed)
00652       // in[sert]/up[date]/de[lete]/cr[eate]/dr[op]/be[gin]/co[mmit]/ro[llback]/{c[all]/}c[all]
00653       // (write stored procedure call)
00654       String lower = sql.substring(0, 2).toLowerCase();
00655       if (lower.equals("in"))
00656       { // insert
00657         AbstractWriteRequest wr = new InsertRequest(sql, escapeProcessing,
00658             timeout, "\n");
00659         wr.setLogin(user);
00660         if (logger.isDebugEnabled())
00661           logger.debug("insert request: " + sql);
00662         setDriverProcessedAndSkeleton(wr);
00663         if (transactionId != 0)
00664         {
00665           wr.setIsAutoCommit(false);
00666           wr.setTransactionId(transactionId);
00667         }
00668         else
00669           wr.setIsAutoCommit(true);
00670         task = new RecoveryTask(transactionId, id, new WriteRequestTask(1, 1,
00671             wr));
00672       }
00673       else if (lower.equals("up"))
00674       { // update
00675         AbstractWriteRequest wr = new UpdateRequest(sql, escapeProcessing,
00676             timeout, "\n");
00677         wr.setLogin(user);
00678         setDriverProcessedAndSkeleton(wr);
00679         if (logger.isDebugEnabled())
00680           logger.debug("update request: " + sql);
00681         if (transactionId != 0)
00682         {
00683           wr.setIsAutoCommit(false);
00684           wr.setTransactionId(transactionId);
00685         }
00686         else
00687           wr.setIsAutoCommit(true);
00688         task = new RecoveryTask(transactionId, id, new WriteRequestTask(1, 1,
00689             wr));
00690       }
00691       else if (lower.equals("de"))
00692       { // delete
00693         AbstractWriteRequest wr = new DeleteRequest(sql, escapeProcessing,
00694             timeout, "\n");
00695         wr.setLogin(user);
00696         setDriverProcessedAndSkeleton(wr);
00697         if (logger.isDebugEnabled())
00698           logger.debug("delete request: " + sql);
00699         if (transactionId != 0)
00700         {
00701           wr.setIsAutoCommit(false);
00702           wr.setTransactionId(transactionId);
00703         }
00704         else
00705           wr.setIsAutoCommit(true);
00706         task = new RecoveryTask(transactionId, id, new WriteRequestTask(1, 1,
00707             wr));
00708       }
00709       else if (lower.equals("cr"))
00710       { // create
00711         AbstractWriteRequest wr = new CreateRequest(sql, escapeProcessing,
00712             timeout, "\n");
00713         wr.setLogin(user);
00714         setDriverProcessedAndSkeleton(wr);
00715         if (logger.isDebugEnabled())
00716           logger.debug("create request: " + sql);
00717         if (transactionId != 0)
00718         {
00719           wr.setIsAutoCommit(false);
00720           wr.setTransactionId(transactionId);
00721         }
00722         else
00723           wr.setIsAutoCommit(true);
00724         task = new RecoveryTask(transactionId, id, new WriteRequestTask(1, 1,
00725             wr));
00726       }
00727       else if (lower.equals("al"))
00728       { // alter
00729         AbstractWriteRequest wr = new AlterRequest(sql, escapeProcessing,
00730             timeout, "\n");
00731         wr.setLogin(user);
00732         setDriverProcessedAndSkeleton(wr);
00733         if (logger.isDebugEnabled())
00734           logger.debug("alter request: " + sql);
00735         if (transactionId != 0)
00736         {
00737           wr.setIsAutoCommit(false);
00738           wr.setTransactionId(transactionId);
00739         }
00740         else
00741           wr.setIsAutoCommit(true);
00742         task = new RecoveryTask(transactionId, id, new WriteRequestTask(1, 1,
00743             wr));
00744       }
00745       else if (lower.equals("dr"))
00746       { // drop
00747         AbstractWriteRequest wr = new DropRequest(sql, escapeProcessing,
00748             timeout, "\n");
00749         wr.setLogin(user);
00750         setDriverProcessedAndSkeleton(wr);
00751         if (logger.isDebugEnabled())
00752           logger.debug("drop request: " + sql);
00753         if (transactionId != 0)
00754         {
00755           wr.setIsAutoCommit(false);
00756           wr.setTransactionId(transactionId);
00757         }
00758         else
00759           wr.setIsAutoCommit(true);
00760         task = new RecoveryTask(transactionId, id, new WriteRequestTask(1, 1,
00761             wr));
00762       }
00763       else if (lower.equals("be"))
00764       { // begin
00765         task = new RecoveryTask(transactionId, id, new BeginTask(1, 1,
00766             (long) timeout * 1000, user, transactionId));
00767         if (logger.isDebugEnabled())
00768           logger.debug("begin transaction: " + transactionId);
00769       }
00770       else if (lower.equals("co"))
00771       { // commit
00772         task = new RecoveryTask(transactionId, id, new CommitTask(1, 1,
00773             (long) timeout * 1000, user, transactionId));
00774         if (logger.isDebugEnabled())
00775           logger.debug("commit transaction: " + transactionId);
00776       }
00777       else if (lower.equals("ro"))
00778       { // rollback
00779         task = new RecoveryTask(transactionId, id, new RollbackTask(1, 1,
00780             (long) timeout * 1000, user, transactionId));
00781         if (logger.isDebugEnabled())
00782           logger.debug("rollback transaction: " + transactionId);
00783       }
00784       else if (lower.equals("{c"))
00785       { // read stored procedure call "{call ...}"
00786         StoredProcedure proc = new StoredProcedure(sql, escapeProcessing,
00787             timeout, "\n");
00788         proc.setLogin(user);
00789         setDriverProcessedAndSkeleton(proc);
00790         if (logger.isDebugEnabled())
00791           logger.debug("read stored procedure call: " + sql);
00792         if (transactionId != 0)
00793         {
00794           proc.setIsAutoCommit(false);
00795           proc.setTransactionId(transactionId);
00796         }
00797         else
00798           proc.setIsAutoCommit(true);
00799         task = new RecoveryTask(transactionId, id, new ReadStoredProcedureTask(
00800             1, 1, proc, null));
00801       }
00802       else if (lower.equals("}c"))
00803       { // write stored procedure call,
00804         // we must replace "}call ...}" with "{call ...}"
00805         StringBuffer writeCall = new StringBuffer(sql);
00806         writeCall.setCharAt(0, '{');
00807         StoredProcedure proc = new StoredProcedure(writeCall.toString(),
00808             escapeProcessing, timeout, "\n");
00809         proc.setLogin(user);
00810         setDriverProcessedAndSkeleton(proc);
00811         if (logger.isDebugEnabled())
00812           logger.debug("write stored procedure call: " + sql);
00813         if (transactionId != 0)
00814         {
00815           proc.setIsAutoCommit(false);
00816           proc.setTransactionId(transactionId);
00817         }
00818         else
00819           proc.setIsAutoCommit(true);
00820         task = new RecoveryTask(transactionId, id,
00821             new WriteStoredProcedureTask(1, 1, proc));
00822       }
00823       else
00824         throw new SQLException(Translate.get("recovery.jdbc.sql.unkwown", sql));
00825     }
00826     catch (SQLException e)
00827     {
00828       throw new SQLException(Translate.get("recovery.jdbc.recover.failed", e));
00829     }
00830     finally
00831     {
00832       try
00833       {
00834         if (stmt != null)
00835           stmt.close();
00836       }
00837       catch (Exception ignore)
00838       {
00839       }
00840     }
00841     return task;
00842   }
00843 
00844   /**
00845    * Set the driverProcessed flag of the given request according to its SQL
00846    * content and rebuild the SQL skeleton if necessary.
00847    * 
00848    * @param request Request to process
00849    */
00850   private void setDriverProcessedAndSkeleton(AbstractRequest request)
00851   {
00852     String sql = request.getSQL();
00853     boolean isDriverProcessed = sql
00854         .indexOf(org.objectweb.cjdbc.driver.Connection.END_PARAM_TAG) == -1;
00855     request.setDriverProcessed(isDriverProcessed);
00856     if (isDriverProcessed)
00857       return; // No need to set the skeleton
00858 
00859     request.setSqlSkeleton(recreateSkeleton(sql));
00860   }
00861 
00862   /**
00863    * Recreate the skeleton of the query from the query itself with the param tag
00864    * 
00865    * @param sql the sql query as it is recorded in the recovery log
00866    * @return the sql skelton with '?' instead of the <?...|...?>tags
00867    */
00868   private String recreateSkeleton(String sql)
00869   {
00870     // Here we have to rebuild the query skeleton to be able to call setXXX on
00871     // the PreparedStatement
00872     StringBuffer skeleton = new StringBuffer();
00873     int start = 0;
00874     int end;
00875     while ((end = sql.indexOf(
00876         org.objectweb.cjdbc.driver.Connection.START_PARAM_TAG, start)) != -1)
00877     {
00878       skeleton.append(sql.substring(start, end)).append('?');
00879       start = sql.indexOf(org.objectweb.cjdbc.driver.Connection.END_PARAM_TAG,
00880           end);
00881       if (start == -1)
00882         throw new RuntimeException("Malformed query in recovery log: " + sql);
00883       else
00884         start += org.objectweb.cjdbc.driver.Connection.END_PARAM_TAG.length();
00885     }
00886     if (start < sql.length())
00887       skeleton.append(sql.substring(start));
00888     return skeleton.toString();
00889   }
00890 
00891   /**
00892    * Removes all rollbacked transaction from the recovery log.
00893    * 
00894    * @exception SQLException if an error occurs.
00895    */
00896   public void cleanRecoveryLog() throws SQLException
00897   {
00898     PreparedStatement stmt = null;
00899 
00900     // Remove the rollback statements and associated requests from the database
00901     ResultSet rs = null;
00902     try
00903     {
00904       // Get the list of transaction ids on which a rollback occurred
00905       stmt = connection.prepareStatement("SELECT transaction_id FROM "
00906           + logTableName + " WHERE sql LIKE ?");
00907       stmt.setString(1, "rollback");
00908       rs = stmt.executeQuery();
00909     }
00910     catch (SQLException e)
00911     {
00912       try
00913       {
00914         if (stmt != null)
00915           stmt.close();
00916       }
00917       catch (Exception ignore)
00918       {
00919       }
00920       throw new SQLException("Unable get rollback statements : " + e);
00921     }
00922     PreparedStatement pstmt = null;
00923     long transactionId = -1;
00924     try
00925     {
00926       // remove the rollbacked transaction from the database
00927       while (rs.next())
00928       {
00929         transactionId = rs.getLong("transaction_Id");
00930         pstmt = connection.prepareStatement("DELETE FROM " + logTableName
00931             + " WHERE transaction_id=?");
00932         pstmt.setLong(1, transactionId);
00933         pstmt.executeUpdate();
00934         pstmt.close();
00935       }
00936       rs.close();
00937       stmt.close();
00938     }
00939     catch (SQLException e)
00940     {
00941       throw new SQLException(Translate.get(
00942           "recovery.jdbc.transaction.remove.failed", new String[]{
00943               String.valueOf(transactionId), e.getMessage()}));
00944     }
00945     finally
00946     {
00947       try
00948       {
00949         if (stmt != null)
00950           stmt.close();
00951       }
00952       catch (Exception ignore)
00953       {
00954       }
00955       try
00956       {
00957         if (pstmt != null)
00958           pstmt.close();
00959       }
00960       catch (Exception ignore)
00961       {
00962       }
00963 
00964     }
00965   }
00966 
00967   //
00968   //
00969   // Checkpoints management
00970   //
00971   //
00972 
00973   /**
00974    * @see org.objectweb.cjdbc.controller.recoverylog.AbstractRecoveryLog#getCheckpointNames()
00975    */
00976   public ArrayList getCheckpointNames() throws SQLException
00977   {
00978     PreparedStatement stmt = null;
00979 
00980     // Wait for transaction to finish
00981     waitForTransactionsEnd(true);
00982 
00983     try
00984     {
00985       if (logger.isDebugEnabled())
00986         logger.debug("Retrieving checkpoint names list");
00987       stmt = connection.prepareStatement("SELECT name from "
00988           + checkpointTableName);
00989       ResultSet rs = stmt.executeQuery();
00990       ArrayList list = new ArrayList();
00991       while (rs.next())
00992       {
00993         list.add(rs.getString(1));
00994       }
00995       rs.close();
00996       return list;
00997     }
00998     catch (Exception e)
00999     {
01000       throw new SQLException(Translate.get(
01001           "recovery.jdbc.checkpoint.list.failed", e));
01002     }
01003     finally
01004     {
01005       try
01006       {
01007         if (stmt != null)
01008           stmt.close();
01009       }
01010       catch (SQLException ignore)
01011       {
01012       }
01013     }
01014   }
01015 
01016   /**
01017    * Checks if a checkpoint with the name checkpointName is already stored in
01018    * the database.
01019    * 
01020    * @param checkpointName name of the checkpoint.
01021    * @return ResultSet empty if no checkpoint was found.
01022    */
01023   private boolean validCheckpointName(String checkpointName)
01024       throws SQLException
01025   {
01026     PreparedStatement stmt = null;
01027     ResultSet rs = null;
01028     try
01029     {
01030       stmt = connection.prepareStatement("SELECT * FROM " + checkpointTableName
01031           + " WHERE name LIKE ?");
01032       stmt.setString(1, checkpointName);
01033       rs = stmt.executeQuery();
01034 
01035       // If the query returned any rows, the checkpoint name is already
01036       // in use and therefore invalid.
01037       boolean checkpointExists = rs.next();
01038       rs.close();
01039       return !checkpointExists;
01040     }
01041     catch (SQLException e)
01042     {
01043       throw new SQLException(Translate.get(
01044           "recovery.jdbc.checkpoint.check.failed", e));
01045     }
01046     finally
01047     {
01048       try
01049       {
01050         if (stmt != null)
01051           stmt.close();
01052       }
01053       catch (SQLException ignore)
01054       {
01055       }
01056     }
01057   }
01058 
01059   /**
01060    * @see org.objectweb.cjdbc.controller.recoverylog.AbstractRecoveryLog#storeCheckpoint(String)
01061    */
01062   public void storeCheckpoint(String checkpointName) throws SQLException
01063   {
01064     storeCheckpoint(checkpointName, logTableId);
01065   }
01066 
01067   /**
01068    * @see org.objectweb.cjdbc.controller.recoverylog.AbstractRecoveryLog#storeCheckpoint(String,
01069    *      long)
01070    */
01071   public void storeCheckpoint(String checkpointName, long requestId)
01072       throws SQLException
01073   {
01074     PreparedStatement stmt = null;
01075 
01076     // Check if a checkpoint with the name checkpointName already exists
01077     if (!validCheckpointName(checkpointName))
01078     {
01079       throw new SQLException(Translate.get(
01080           "recovery.jdbc.checkpoint.duplicate", checkpointName));
01081     }
01082 
01083     // Wait for transaction to finish
01084     waitForTransactionsEnd(true);
01085 
01086     try
01087     {
01088       if (logger.isDebugEnabled())
01089         logger.debug("Storing checkpoint " + checkpointName + " at request id "
01090             + requestId);
01091       stmt = connection.prepareStatement("INSERT INTO " + checkpointTableName
01092           + " VALUES(?,?)");
01093       stmt.setString(1, checkpointName);
01094       stmt.setLong(2, requestId);
01095       stmt.executeUpdate();
01096     }
01097     catch (SQLException e)
01098     {
01099       throw new SQLException(Translate.get(
01100           "recovery.jdbc.checkpoint.store.failed", new String[]{checkpointName,
01101               e.getMessage()}));
01102     }
01103     finally
01104     {
01105       try
01106       {
01107         if (stmt != null)
01108           stmt.close();
01109       }
01110       catch (Exception ignore)
01111       {
01112       }
01113     }
01114   }
01115 
01116   /**
01117    * @see org.objectweb.cjdbc.controller.recoverylog.AbstractRecoveryLog#removeCheckpoint(java.lang.String)
01118    */
01119   public void removeCheckpoint(String checkpointName) throws SQLException
01120   {
01121     PreparedStatement stmt = null;
01122 
01123     // Wait for transaction to finish
01124     waitForTransactionsEnd(true);
01125 
01126     try
01127     {
01128       // 1. Get the reference point to delete
01129       stmt = connection.prepareStatement("SELECT * FROM " + checkpointTableName
01130           + " WHERE name LIKE ?");
01131       stmt.setString(1, checkpointName);
01132       ResultSet rs = stmt.executeQuery();
01133       boolean checkpointExists = rs.next();
01134       if (!checkpointExists)
01135       {
01136         rs.close();
01137         stmt.close();
01138         throw new SQLException("Checkpoint " + checkpointName
01139             + " does not exist");
01140       }
01141 
01142       int requestId = rs.getInt("request_id");
01143       rs.close();
01144       stmt.close();
01145 
01146       // Delete all entries below
01147       stmt = connection.prepareStatement("DELETE FROM " + logTableName
01148           + " WHERE ID <= ?");
01149       stmt.setInt(1, requestId);
01150       stmt.executeUpdate();
01151       stmt.close();
01152 
01153       // Delete checkpoint name
01154       stmt = connection.prepareStatement("DELETE FROM " + checkpointTableName
01155           + " WHERE name like ?");
01156       stmt.setString(1, checkpointName);
01157       stmt.executeUpdate();
01158       stmt.close();
01159     }
01160     catch (SQLException e)
01161     {
01162       throw new SQLException(Translate.get(
01163           "recovery.jdbc.checkpoint.remove.failed", new String[]{
01164               checkpointName, e.getMessage()}));
01165     }
01166     finally
01167     {
01168       try
01169       {
01170         if (stmt != null)
01171           stmt.close();
01172       }
01173       catch (Exception ignore)
01174       {
01175       }
01176 
01177     }
01178 
01179   }
01180 
01181   /**
01182    * Wait for current transactions to complete. Current transactions can be
01183    * forced to be rollbacked if necessary.
01184    * 
01185    * @param forceRollback true if currently open transactions must be
01186    *          rollbacked.
01187    */
01188   private void waitForTransactionsEnd(boolean forceRollback)
01189   {
01190     if (forceRollback)
01191     {
01192       loggerThread.rollbackTransactions();
01193     }
01194     else
01195     {
01196       synchronized (loggerThread)
01197       {
01198         while (!loggerThread.getLogQueueIsEmpty())
01199         {
01200           try
01201           {
01202             wait();
01203           }
01204           catch (Exception e)
01205           {
01206             logger.warn("Exception " + e
01207                 + " while waiting for end of transactions");
01208           }
01209         }
01210       }
01211     }
01212   }
01213 
01214   /**
01215    * @see org.objectweb.cjdbc.controller.recoverylog.AbstractRecoveryLog#getBackendRecoveryInfo(java.lang.String,
01216    *      java.lang.String)
01217    */
01218   public BackendRecoveryInfo getBackendRecoveryInfo(String databaseName,
01219       String backendName)
01220   {
01221     PreparedStatement stmt = null;
01222     String checkpoint = null;
01223     int backendState = BackendState.UNKNOWN;
01224     try
01225     {
01226       // 1. Get the reference point to delete
01227       stmt = connection.prepareStatement("SELECT * FROM " + backendTableName
01228           + " WHERE backendName LIKE ? AND databaseName LIKE ?");
01229       stmt.setString(1, backendName);
01230       stmt.setString(2, databaseName);
01231       ResultSet rs = stmt.executeQuery();
01232 
01233       if (rs.next())
01234       {
01235         checkpoint = rs.getString("checkpointName");
01236         backendState = rs.getInt("backendState");
01237       }
01238       rs.close();
01239     }
01240     catch (SQLException e)
01241     {
01242       logger.info(
01243           "An error occured while retrieving backend recovery information", e);
01244     }
01245     finally
01246     {
01247       try
01248       {
01249         if (stmt != null)
01250           stmt.close();
01251       }
01252       catch (Exception ignore)
01253       {
01254       }
01255     }
01256     return new BackendRecoveryInfo(backendName, checkpoint, backendState,
01257         databaseName);
01258   }
01259 
01260   /**
01261    * @see org.objectweb.cjdbc.controller.recoverylog.AbstractRecoveryLog#storeBackendRecoveryInfo(String,
01262    *      BackendRecoveryInfo)
01263    */
01264   public void storeBackendRecoveryInfo(String databaseName,
01265       BackendRecoveryInfo backendRecoveryInfo) throws SQLException
01266   {
01267     PreparedStatement stmt = null;
01268     PreparedStatement stmt2 = null;
01269     if ((backendRecoveryInfo.getCheckpoint() == null)
01270         || ((backendRecoveryInfo.getBackendState() != BackendState.DISABLED) && (backendRecoveryInfo
01271             .getBackendState() != BackendState.UNKNOWN)))
01272       backendRecoveryInfo.setCheckpoint(""); // No checkpoint
01273     else
01274     { // Check checkpoint name validity
01275       getCheckpointRequestId(backendRecoveryInfo.getCheckpoint());
01276     }
01277 
01278     try
01279     {
01280       // 1. Get the reference point to delete
01281       stmt = connection.prepareStatement("SELECT * FROM " + backendTableName
01282           + " WHERE backendName LIKE ? and databaseName LIKE ?");
01283       stmt.setString(1, backendRecoveryInfo.getBackendName());
01284       stmt.setString(2, databaseName);
01285       ResultSet rs = stmt.executeQuery();
01286       boolean mustUpdate = rs.next();
01287       rs.close();
01288       if (!mustUpdate)
01289       {
01290         stmt2 = connection.prepareStatement("INSERT INTO " + backendTableName
01291             + " values(?,?,?,?)");
01292         stmt2.setString(1, databaseName);
01293         stmt2.setString(2, backendRecoveryInfo.getBackendName());
01294         stmt2.setInt(3, backendRecoveryInfo.getBackendState());
01295         stmt2.setString(4, backendRecoveryInfo.getCheckpoint());
01296         if (stmt2.executeUpdate() != 1)
01297           throw new SQLException(
01298               "Error while inserting new backend reference. Incorrect number of rows");
01299       }
01300       else
01301       {
01302         stmt2 = connection
01303             .prepareStatement("UPDATE "
01304                 + backendTableName
01305                 + " set backendState=?,checkpointName=? where backendName=? and databaseName=?");
01306         stmt2.setInt(1, backendRecoveryInfo.getBackendState());
01307         stmt2.setString(2, backendRecoveryInfo.getCheckpoint());
01308         stmt2.setString(3, backendRecoveryInfo.getBackendName());
01309         stmt2.setString(4, databaseName);
01310         if (stmt2.executeUpdate() != 1)
01311           throw new SQLException(
01312               "Error while updating backend reference. Incorrect number of rows");
01313       }
01314     }
01315     catch (SQLException e)
01316     {
01317       throw new SQLException("Unable to update checkpoint '"
01318           + backendRecoveryInfo.getCheckpoint() + "' for backend:"
01319           + backendRecoveryInfo.getBackendName());
01320     }
01321     finally
01322     {
01323       try
01324       {
01325         if (stmt != null)
01326           stmt.close();
01327       }
01328       catch (Exception ignore)
01329       {
01330       }
01331       try
01332       {
01333         if (stmt2 != null)
01334           stmt2.close();
01335       }
01336       catch (Exception ignore)
01337       {
01338       }
01339     }
01340   }
01341 
01342   //
01343   // 
01344   // Recovery log database tables management
01345   //
01346   //
01347 
01348   /**
01349    * Checks if the recovery log and checkpoint tables exist, and create them if
01350    * they do not exist. This method also starts the logger thread.
01351    */
01352   public void checkRecoveryLogTables()
01353   {
01354     try
01355     {
01356       intializeDatabase();
01357       pstmt = connection.prepareStatement("INSERT INTO " + logTableName
01358           + " VALUES(?,?,?,?)");
01359     }
01360     catch (SQLException e)
01361     {
01362       throw new RuntimeException("Unable to initialize the database: " + e);
01363     }
01364 
01365     // Start the logger thread
01366     loggerThread = new JDBCLoggerThread(pstmt, logger);
01367     loggerThread.start();
01368   }
01369 
01370   /**
01371    * Returns the backendTableName value.
01372    * 
01373    * @return Returns the backendTableName.
01374    */
01375   public String getBackendTableName()
01376   {
01377     return backendTableName;
01378   }
01379 
01380   /**
01381    * Returns the checkpointTableName value.
01382    * 
01383    * @return Returns the checkpointTableName.
01384    */
01385   public String getCheckpointTableName()
01386   {
01387     return checkpointTableName;
01388   }
01389 
01390   /**
01391    * Returns the logTableName value.
01392    * 
01393    * @return Returns the logTableName.
01394    */
01395   public String getLogTableName()
01396   {
01397     return logTableName;
01398   }
01399 
01400   /**
01401    * Sets the backend table create statement
01402    * 
01403    * @param tableName the backend table name
01404    * @param checkpointNameType type for the checkpointName column
01405    * @param backendNameType type for the backendName column
01406    * @param backendStateType type for the backendState column
01407    * @param databaseNameType type for the databaseName column
01408    * @param extraStatement like primary keys
01409    */
01410   public void setBackendTableCreateStatement(String tableName,
01411       String checkpointNameType, String backendNameType,
01412       String backendStateType, String databaseNameType, String extraStatement)
01413   {
01414     this.backendTableName = tableName;
01415     this.backendTableCreateStatement = "CREATE TABLE " + backendTableName
01416         + " (databaseName " + databaseNameType + ", backendName "
01417         + backendNameType + ",backendState " + backendStateType
01418         + ", checkpointName " + checkpointNameType + " " + extraStatement + ")";
01419 
01420     if (logger.isDebugEnabled())
01421       logger.debug(Translate.get("recovery.jdbc.backendtable.statement",
01422           backendTableCreateStatement));
01423   }
01424 
01425   /**
01426    * Sets the checkpoint table name and create statement.
01427    * 
01428    * @param tableName name of the checkpoint table.
01429    * @param nameType type for the name column
01430    * @param requestIdType type for the requestId column
01431    * @param extraStatement like primary keys
01432    */
01433   public void setCheckpointTableCreateStatement(String tableName,
01434       String nameType, String requestIdType, String extraStatement)
01435   {
01436     this.checkpointTableName = tableName;
01437     this.nameType = nameType;
01438     this.requestIdType = requestIdType;
01439     this.checkextraStatement = extraStatement;
01440     // CREATE TABLE tableName (
01441     //   name checkpointNameColumnType,
01442     //   request_id requestIdColumnType,
01443     //   extraStatement)
01444 
01445     String checkpointTableCreateStatement = "CREATE TABLE " + tableName
01446         + " (name " + nameType + ",request_id " + requestIdType
01447         + extraStatement + ")";
01448     if (logger.isDebugEnabled())
01449       logger.debug(Translate.get("recovery.jdbc.checkpointtable.statement",
01450           checkpointTableCreateStatement));
01451 
01452     this.checkpointTableCreateStatement = checkpointTableCreateStatement;
01453   }
01454 
01455   /**
01456    * Sets the log table name and create statement.
01457    * 
01458    * @param tableName of the log table
01459    * @param idType of the id column
01460    * @param vloginType of the login column
01461    * @param sqlName name of the sql statement column
01462    * @param sqlType of the sql column
01463    * @param transactionIdType of the transaction column
01464    * @param extraStatement like primary keys ...
01465    */
01466   public void setLogTableCreateStatement(String tableName, String idType,
01467       String vloginType, String sqlName, String sqlType,
01468       String transactionIdType, String extraStatement)
01469   {
01470     this.logTableName = tableName;
01471     this.idType = idType;
01472     this.vloginType = vloginType;
01473     this.sqlColumnName = sqlName;
01474     this.sqlType = sqlType;
01475     this.transactionIdType = transactionIdType;
01476     this.logextraStatement = extraStatement;
01477     String logTableCreateStatement = "CREATE TABLE " + tableName + " (id "
01478         + idType + ",vlogin " + vloginType + "," + sqlColumnName + " "
01479         + sqlType + ",transaction_id " + transactionIdType + extraStatement
01480         + ")";
01481     if (logger.isDebugEnabled())
01482       logger.debug(Translate.get("recovery.jdbc.logtable.statement",
01483           logTableCreateStatement));
01484 
01485     this.logTableCreateStatement = logTableCreateStatement;
01486   }
01487 
01488   //
01489   //
01490   // Info/Monitoring/Debug related functions
01491   //
01492   //
01493 
01494   /**
01495    * @see org.objectweb.cjdbc.controller.recoverylog.AbstractRecoveryLog#getData()
01496    */
01497   public String[][] getData()
01498   {
01499     Statement stmt = null;
01500     ResultSet rs = null;
01501     try
01502     {
01503       stmt = connection.createStatement();
01504       rs = stmt.executeQuery("select * from " + logTableName);
01505       ArrayList list = new ArrayList();
01506       while (rs.next())
01507       {
01508         // 3: Query 2: User 1: ID 4: TID
01509         list.add(new String[]{rs.getString(3), rs.getString(2),
01510             rs.getString(1), rs.getString(4)});
01511       }
01512       String[][] result = new String[list.size()][4];
01513       for (int i = 0; i < list.size(); i++)
01514         result[i] = (String[]) list.get(i);
01515       return result;
01516     }
01517     catch (SQLException e)
01518     {
01519       return null;
01520     }
01521     finally
01522     {
01523       try
01524       {
01525         rs.close();
01526       }
01527       catch (SQLException ignore)
01528       {
01529       }
01530       try
01531       {
01532         stmt.close();
01533       }
01534       catch (SQLException ignore)
01535       {
01536       }
01537     }
01538   }
01539 
01540   /**
01541    * @see org.objectweb.cjdbc.controller.jmx.AbstractStandardMBean#getAssociatedString()
01542    */
01543   public String getAssociatedString()
01544   {
01545     return "jdbcrecoverylog";
01546   }
01547 
01548   /**
01549    * @see org.objectweb.cjdbc.controller.recoverylog.AbstractRecoveryLog#getXmlImpl()
01550    */
01551   public String getXmlImpl()
01552   {
01553     StringBuffer info = new StringBuffer();
01554     info.append("<" + DatabasesXmlTags.ELT_JDBCRecoveryLog + " "
01555         + DatabasesXmlTags.ATT_driver + "=\"" + driverClassName + "\" "
01556         + DatabasesXmlTags.ATT_url + "=\"" + url + "\" ");
01557     if (driverName != null)
01558     {
01559       info.append(DatabasesXmlTags.ATT_driverPath + "=\"" + driverName + "\" ");
01560     }
01561     info.append(DatabasesXmlTags.ATT_login + "=\"" + login + "\" "
01562         + DatabasesXmlTags.ATT_password + "=\"" + password + "\" "
01563         + DatabasesXmlTags.ATT_requestTimeout + "=\"" + (timeout / 1000)
01564         + "\">");
01565     // Recovery Log table
01566     info.append("<" + DatabasesXmlTags.ELT_RecoveryLogTable + " "
01567         + DatabasesXmlTags.ATT_tableName + "=\"" + logTableName + "\"" + " "
01568         + DatabasesXmlTags.ATT_idColumnType + "=\"" + idType + "\"" + " "
01569         + DatabasesXmlTags.ATT_vloginColumnType + "=\"" + vloginType + "\""
01570         + " " + DatabasesXmlTags.ATT_sqlColumnType + "=\"" + sqlType + "\""
01571         + " " + DatabasesXmlTags.ATT_transactionIdColumnType + "=\""
01572         + transactionIdType + "\"" + " "
01573         + DatabasesXmlTags.ATT_extraStatementDefinition + "=\""
01574         + logextraStatement + "\"/>");
01575     // Checkpoint table
01576     info.append("<" + DatabasesXmlTags.ELT_CheckpointTable + " "
01577         + DatabasesXmlTags.ATT_tableName + "=\"" + checkpointTableName + "\""
01578         + " " + DatabasesXmlTags.ATT_checkpointNameColumnType + "=\""
01579         + nameType + "\"" + " " + DatabasesXmlTags.ATT_requestIdColumnType
01580         + "=\"" + requestIdType + "\"" + " "
01581         + DatabasesXmlTags.ATT_extraStatementDefinition + "=\""
01582         + checkextraStatement + "\"" + "/>");
01583     // BackendLog table
01584     info.append("<" + DatabasesXmlTags.ELT_BackendTable + " "
01585         + DatabasesXmlTags.ATT_tableName + "=\"" + backendTableName + "\""
01586         + "/>");
01587 
01588     info.append("</" + DatabasesXmlTags.ELT_JDBCRecoveryLog + ">");
01589 
01590     return info.toString();
01591   }
01592 
01593 }

Generated on Mon Apr 11 22:01:32 2005 for C-JDBC by  doxygen 1.3.9.1