KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > objectweb > cjdbc > controller > recoverylog > RecoveryLog


1 /**
2  * C-JDBC: Clustered JDBC.
3  * Copyright (C) 2002-2005 French National Institute For Research In Computer
4  * Science And Control (INRIA).
5  * Contact: c-jdbc@objectweb.org
6  *
7  * This library is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU Lesser General Public License as published by the
9  * Free Software Foundation; either version 2.1 of the License, or any later
10  * version.
11  *
12  * This library is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
15  * for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public License
18  * along with this library; if not, write to the Free Software Foundation,
19  * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
20  *
21  * Initial developer(s): Emmanuel Cecchet.
22  * Contributor(s): Julie Marguerite, Greg Ward, Jess Sightler, Jean-Bernard van Zuylen, Charles Cordingley.
23  */

24
25 package org.objectweb.cjdbc.controller.recoverylog;
26
27 import java.sql.Connection JavaDoc;
28 import java.sql.DatabaseMetaData JavaDoc;
29 import java.sql.PreparedStatement JavaDoc;
30 import java.sql.ResultSet JavaDoc;
31 import java.sql.SQLException JavaDoc;
32 import java.sql.Statement JavaDoc;
33 import java.util.ArrayList JavaDoc;
34
35 import org.objectweb.cjdbc.common.exceptions.VirtualDatabaseException;
36 import org.objectweb.cjdbc.common.i18n.Translate;
37 import org.objectweb.cjdbc.common.log.Trace;
38 import org.objectweb.cjdbc.common.shared.BackendState;
39 import org.objectweb.cjdbc.common.shared.DumpInfo;
40 import org.objectweb.cjdbc.common.sql.AbstractRequest;
41 import org.objectweb.cjdbc.common.sql.AbstractWriteRequest;
42 import org.objectweb.cjdbc.common.sql.AlterRequest;
43 import org.objectweb.cjdbc.common.sql.CreateRequest;
44 import org.objectweb.cjdbc.common.sql.DeleteRequest;
45 import org.objectweb.cjdbc.common.sql.DropRequest;
46 import org.objectweb.cjdbc.common.sql.InsertRequest;
47 import org.objectweb.cjdbc.common.sql.StoredProcedure;
48 import org.objectweb.cjdbc.common.sql.UpdateRequest;
49 import org.objectweb.cjdbc.common.xml.DatabasesXmlTags;
50 import org.objectweb.cjdbc.common.xml.XmlComponent;
51 import org.objectweb.cjdbc.controller.connection.DriverManager;
52 import org.objectweb.cjdbc.controller.loadbalancer.tasks.BeginTask;
53 import org.objectweb.cjdbc.controller.loadbalancer.tasks.CommitTask;
54 import org.objectweb.cjdbc.controller.loadbalancer.tasks.ReadStoredProcedureTask;
55 import org.objectweb.cjdbc.controller.loadbalancer.tasks.ReleaseSavepointTask;
56 import org.objectweb.cjdbc.controller.loadbalancer.tasks.RollbackTask;
57 import org.objectweb.cjdbc.controller.loadbalancer.tasks.RollbackToSavepointTask;
58 import org.objectweb.cjdbc.controller.loadbalancer.tasks.SavepointTask;
59 import org.objectweb.cjdbc.controller.loadbalancer.tasks.WriteRequestTask;
60 import org.objectweb.cjdbc.controller.loadbalancer.tasks.WriteStoredProcedureTask;
61 import org.objectweb.cjdbc.controller.recoverylog.events.LogEntry;
62 import org.objectweb.cjdbc.controller.recoverylog.events.LogRequestEvent;
63 import org.objectweb.cjdbc.controller.recoverylog.events.LogRollbackEvent;
64 import org.objectweb.cjdbc.controller.recoverylog.events.ResetLogEvent;
65 import org.objectweb.cjdbc.controller.recoverylog.events.StoreDumpCheckpointEvent;
66 import org.objectweb.cjdbc.controller.recoverylog.events.UnlogRequestEvent;
67 import org.objectweb.cjdbc.controller.requestmanager.TransactionMarkerMetaData;
68
69 /**
70  * Recovery Log using a database accessed through JDBC.
71  *
72  * @author <a HREF="mailto:Emmanuel.Cecchet@inria.fr">Emmanuel Cecchet </a>
73  * @author <a HREF="mailto:Julie.Marguerite@inria.fr">Julie Marguerite </a>
74  * @author <a HREF="mailto:Nicolas.Modrzyk@inrialpes.fr">Nicolas Modrzyk </a>*
75  * @author <a HREF="mailto:jbvanzuylen@transwide.com">Jean-Bernard van Zuylen
76  * </a>
77  * @version 1.0
78  */

79 public class RecoveryLog implements XmlComponent
80 {
81   static Trace logger = Trace
82                                                                             .getLogger("org.objectweb.cjdbc.controller.recoverylog");
83
84   /** Number of backends currently recovering */
85   private long recoveringNb = 0;
86
87   /** Size of the pendingRecoveryTasks queue used by the recover thread */
88   protected int recoveryBatchSize;
89
90   /** Driver class name. */
91   private String JavaDoc driverClassName;
92
93   /** Driver name. */
94   private String JavaDoc driverName;
95
96   /** Driver URL. */
97   private String JavaDoc url;
98
99   /** User's login. */
100   private String JavaDoc login;
101
102   /** User's password. */
103   private String JavaDoc password;
104
105   /** Database connection */
106   private Connection JavaDoc internalConnectionManagedByGetDatabaseConnection = null;
107
108   /**
109    * Recovery log table options.
110    *
111    * @see #setLogTableCreateStatement(String, String, String, String, String,
112    * String, String, String)
113    */

114   private String JavaDoc logTableCreateTable;
115   private String JavaDoc logTableName;
116   private String JavaDoc logTableCreateStatement;
117   private String JavaDoc logTableIdType;
118   private String JavaDoc logTableVloginType;
119   private String JavaDoc logTableSqlColumnName;
120   private String JavaDoc logTableSqlType;
121   private String JavaDoc logTableTransactionIdType;
122   private String JavaDoc logTableExtraStatement;
123
124   /**
125    * Checkpoint table options.
126    *
127    * @see #setCheckpointTableCreateStatement(String, String, String, String,
128    * String)
129    */

130   private String JavaDoc checkpointTableCreateTable;
131   private String JavaDoc checkpointTableName;
132   private String JavaDoc checkpointTableCreateStatement;
133   private String JavaDoc checkpointTableNameType;
134   private String JavaDoc checkpointTableRequestIdType;
135   private String JavaDoc checkpointTableExtraStatement;
136
137   /**
138    * Backend table options
139    *
140    * @see #setBackendTableCreateStatement(String, String, String, String,
141    * String, String, String)
142    */

143   private String JavaDoc backendTableCreateStatement;
144   private String JavaDoc backendTableName;
145   private String JavaDoc backendTableCreateTable;
146   private String JavaDoc backendTableDatabaseName;
147   private String JavaDoc backendTableExtraStatement;
148   private String JavaDoc backendTableCheckpointName;
149   private String JavaDoc backendTableBackendState;
150   private String JavaDoc backendTableBackendName;
151
152   /**
153    * Dump table options
154    *
155    * @see #setDumpTableCreateStatement(String, String, String, String, String,
156    * String, String, String, String, String)
157    */

158   private String JavaDoc dumpTableCreateStatement;
159   private String JavaDoc dumpTableCreateTable;
160   private String JavaDoc dumpTableName;
161   private String JavaDoc dumpTableDumpNameColumnType;
162   private String JavaDoc dumpTableDumpDateColumnType;
163   private String JavaDoc dumpTableDumpPathColumnType;
164   private String JavaDoc dumpTableDumpFormatColumnType;
165   private String JavaDoc dumpTableCheckpointNameColumnType;
166   private String JavaDoc dumpTableBackendNameColumnType;
167   private String JavaDoc dumpTableTablesColumnName;
168   private String JavaDoc dumpTableTablesColumnType;
169   private String JavaDoc dumpTableExtraStatementDefinition;
170
171   /** Current maximum value of the primary key in logTableName. */
172   private long logTableId = 0;
173
174   /** Timeout for SQL requests. */
175   private int timeout;
176
177   private LoggerThread loggerThread;
178
179   /**
180    * Creates a new <code>RecoveryLog</code> instance.
181    *
182    * @param driverName the driverClassName name.
183    * @param driverClassName the driverClassName class name.
184    * @param url the JDBC URL.
185    * @param login the login to use to connect to the database.
186    * @param password the password to connect to the database.
187    * @param requestTimeout timeout in seconds for update queries.
188    * @param recoveryBatchSize number of queries that can be accumulated into a
189    * batch when recovering
190    */

191   public RecoveryLog(String JavaDoc driverName, String JavaDoc driverClassName, String JavaDoc url,
192       String JavaDoc login, String JavaDoc password, int requestTimeout, int recoveryBatchSize)
193   {
194     this.driverName = driverName;
195     this.driverClassName = driverClassName;
196     this.url = url;
197     this.login = login;
198     this.password = password;
199     this.timeout = requestTimeout;
200     if (recoveryBatchSize < 1)
201     {
202       logger
203           .warn("RecoveryBatchSize was set to a value lesser than 1, resetting value to 1.");
204       recoveryBatchSize = 1;
205     }
206     this.recoveryBatchSize = recoveryBatchSize;
207
208     // Connect to the database
209
try
210     {
211       getDatabaseConnection();
212     }
213     catch (SQLException JavaDoc e)
214     {
215       throw new RuntimeException JavaDoc("Unable to connect to the database: " + e);
216     }
217
218     // Logger thread will be created in checkRecoveryLogTables()
219
// after database has been initialized
220
}
221
222   //
223
// Database manipulation and access
224
//
225

226   /**
227    * Gets a connection to the database.
228    *
229    * @return a connection to the database
230    * @exception SQLException if an error occurs.
231    * @see #invalidateInternalConnection()
232    */

233   protected Connection JavaDoc getDatabaseConnection() throws SQLException JavaDoc
234   {
235     try
236     {
237       if (internalConnectionManagedByGetDatabaseConnection == null)
238       {
239         if (logger.isDebugEnabled())
240           logger.debug(Translate.get("recovery.jdbc.connect", new String JavaDoc[]{url,
241               login}));
242         internalConnectionManagedByGetDatabaseConnection = DriverManager
243             .getConnection(url, login, password, driverName, driverClassName);
244       }
245       return internalConnectionManagedByGetDatabaseConnection;
246     }
247     catch (RuntimeException JavaDoc e)
248     {
249       String JavaDoc msg = Translate.get("recovery.jdbc.connect.failed", e);
250       if (logger.isDebugEnabled())
251         logger.debug(msg, e);
252       throw new SQLException JavaDoc(msg);
253     }
254     catch (SQLException JavaDoc e)
255     {
256       invalidateInternalConnection();
257       String JavaDoc msg = Translate.get("recovery.jdbc.connect.failed", e);
258       if (logger.isDebugEnabled())
259         logger.debug(msg, e);
260       throw new SQLException JavaDoc(msg);
261     }
262   }
263
264   /**
265    * Increments the value of logTableId.
266    */

267   private synchronized long incrementLogTableId()
268   {
269     logTableId++;
270     return logTableId;
271   }
272
273   /**
274    * Checks if the tables (log and checkpoint) already exist, and create them if
275    * needed.
276    */

277   private void intializeDatabase() throws SQLException JavaDoc
278   {
279     boolean createLogTable = true;
280     boolean createCheckpointTable = true;
281     boolean createBackendTable = true;
282     boolean createDumpTable = true;
283     Connection JavaDoc connection;
284     // Check if tables exist
285
try
286     {
287       connection = getDatabaseConnection();
288       connection.setAutoCommit(false);
289       // Get DatabaseMetaData
290
DatabaseMetaData JavaDoc metaData = connection.getMetaData();
291
292       // Get a description of tables matching the catalog, schema, table name
293
// and type.
294
// Sending in null for catalog and schema drop them from the selection
295
// criteria. Replace the last argument in the getTables method with
296
// types below to obtain only database tables. (Sending in null
297
// retrieves all types).
298
String JavaDoc[] types = {"TABLE", "VIEW"};
299       ResultSet JavaDoc rs = metaData.getTables(null, null, "%", types);
300
301       // Get tables metadata
302
String JavaDoc tableName;
303       while (rs.next())
304       {
305         // 1 is table catalog, 2 is table schema, 3 is table name, 4 is type
306
tableName = rs.getString(3);
307         if (logger.isDebugEnabled())
308           logger.debug(Translate.get("recovery.jdbc.table.found", tableName));
309         if (tableName.equalsIgnoreCase(logTableName))
310         {
311           if (tableName.compareTo(logTableName) != 0)
312             logger.warn(Translate.get("recovery.jdbc.logtable.case.mismatch",
313                 new String JavaDoc[]{logTableName, tableName}));
314           createLogTable = false;
315           // initialize logTableId
316
PreparedStatement JavaDoc p = null;
317           try
318           {
319             ResultSet JavaDoc result = null;
320             p = connection.prepareStatement("SELECT MAX(id) AS max_id FROM "
321                 + logTableName);
322             result = p.executeQuery();
323             if (result.next())
324               logTableId = result.getLong("max_id");
325             else
326               logTableId = 0;
327             p.close();
328           }
329           catch (SQLException JavaDoc e)
330           {
331             try
332             {
333               if (p != null)
334                 p.close();
335             }
336             catch (Exception JavaDoc ignore)
337             {
338             }
339             throw new RuntimeException JavaDoc(Translate.get(
340                 "recovery.jdbc.logtable.getvalue.failed", e));
341           }
342
343         }
344         if (tableName.equalsIgnoreCase(checkpointTableName))
345         {
346           if (tableName.compareTo(checkpointTableName) != 0)
347             logger.warn(Translate.get(
348                 "recovery.jdbc.checkpointtable.case.mismatch", new String JavaDoc[]{
349                     checkpointTableName, tableName}));
350           createCheckpointTable = false;
351         }
352         else if (tableName.equalsIgnoreCase(backendTableName))
353         {
354           if (tableName.compareTo(backendTableName) != 0)
355             logger.warn(Translate.get(
356                 "recovery.jdbc.backendtable.case.mismatch", new String JavaDoc[]{
357                     backendTableName, tableName}));
358           createBackendTable = false;
359         }
360         else if (tableName.equalsIgnoreCase(dumpTableName))
361         {
362           if (tableName.compareTo(dumpTableName) != 0)
363             logger.warn(Translate.get("recovery.jdbc.dumptable.case.mismatch",
364                 new String JavaDoc[]{backendTableName, tableName}));
365           createDumpTable = false;
366         }
367       }
368       try
369       {
370         connection.commit();
371         connection.setAutoCommit(true);
372       }
373       catch (Exception JavaDoc ignore)
374       {
375         // Read-only transaction we don't care
376
}
377     }
378     catch (SQLException JavaDoc e)
379     {
380       logger.error(Translate.get("recovery.jdbc.table.no.description"), e);
381       throw e;
382     }
383
384     // Create the missing tables
385
Statement JavaDoc stmt = null;
386     if (createLogTable)
387     {
388       if (logger.isInfoEnabled())
389         logger.info(Translate
390             .get("recovery.jdbc.logtable.create", logTableName));
391       try
392       {
393         stmt = connection.createStatement();
394         stmt.executeUpdate(logTableCreateStatement);
395         stmt.close();
396       }
397       catch (SQLException JavaDoc e)
398       {
399         throw new SQLException JavaDoc(Translate.get(
400             "recovery.jdbc.logtable.create.failed", new String JavaDoc[]{logTableName,
401                 e.getMessage()}));
402       }
403     }
404     if (createCheckpointTable)
405     {
406       if (logger.isInfoEnabled())
407         logger.info(Translate.get("recovery.jdbc.checkpointtable.create",
408             checkpointTableName));
409       try
410       {
411         stmt = connection.createStatement();
412         stmt.executeUpdate(checkpointTableCreateStatement);
413         stmt.close();
414       }
415       catch (SQLException JavaDoc e)
416       {
417         throw new SQLException JavaDoc(Translate.get(
418             "recovery.jdbc.checkpointtable.create.failed", new String JavaDoc[]{
419                 logTableName, e.getMessage()}));
420       }
421
422       // Add an initial checkpoint in the table
423
String JavaDoc checkpointName = "Initial_empty_recovery_log";
424       PreparedStatement JavaDoc pstmt = null;
425       try
426       {
427         if (logger.isDebugEnabled())
428           logger.debug("Storing checkpoint " + checkpointName
429               + " at request id " + logTableId);
430         pstmt = connection.prepareStatement("INSERT INTO "
431             + checkpointTableName + " VALUES(?,?)");
432         pstmt.setString(1, checkpointName);
433         pstmt.setLong(2, logTableId);
434         pstmt.executeUpdate();
435         pstmt.close();
436       }
437       catch (SQLException JavaDoc e)
438       {
439         try
440         {
441           if (pstmt != null)
442             pstmt.close();
443         }
444         catch (Exception JavaDoc ignore)
445         {
446         }
447         throw new SQLException JavaDoc(Translate.get(
448             "recovery.jdbc.checkpoint.store.failed", new String JavaDoc[]{
449                 checkpointName, e.getMessage()}));
450       }
451     }
452     if (createBackendTable)
453     {
454       if (logger.isInfoEnabled())
455         logger.info(Translate.get("recovery.jdbc.backendtable.create",
456             backendTableName));
457       try
458       {
459         stmt = connection.createStatement();
460         stmt.executeUpdate(backendTableCreateStatement);
461         stmt.close();
462       }
463       catch (SQLException JavaDoc e)
464       {
465         throw new SQLException JavaDoc(Translate.get(
466             "recovery.jdbc.backendtable.create.failed", new String JavaDoc[]{
467                 logTableName, e.getMessage()}));
468       }
469     }
470     if (createDumpTable)
471     {
472       if (logger.isInfoEnabled())
473         logger.info(Translate.get("recovery.jdbc.dumptable.create",
474             dumpTableName));
475       try
476       {
477         stmt = connection.createStatement();
478         stmt.executeUpdate(dumpTableCreateStatement);
479         stmt.close();
480       }
481       catch (SQLException JavaDoc e)
482       {
483         throw new SQLException JavaDoc(Translate.get(
484             "recovery.jdbc.dumptable.create.failed", new String JavaDoc[]{
485                 dumpTableName, e.getMessage()}));
486       }
487     }
488   }
489
490   /**
491    * Invalidate the connection when an error occurs so that the next call to
492    * getDatabaseConnection() re-allocates a new connection.
493    *
494    * @see #getDatabaseConnection()
495    */

496   protected void invalidateInternalConnection()
497   {
498     try
499     {
500       internalConnectionManagedByGetDatabaseConnection.close();
501     }
502     catch (Exception JavaDoc ignore)
503     {
504     }
505     internalConnectionManagedByGetDatabaseConnection = null;
506   }
507
508   //
509
//
510
// Logging related methods
511
//
512
//
513

514   /**
515    * Log a transaction abort. This is used only for transaction that were
516    * started but where no request was executed, which is in fact an empty
517    * transaction. The underlying implementation might safely discard the
518    * corresponding begin from the log as an optimization.
519    *
520    * @param tm The transaction marker metadata
521    * @return the identifier of the entry in the recovery log
522    */

523   public long logAbort(TransactionMarkerMetaData tm)
524   {
525     // We have to perform exactly the same job as a rollback
526
return logRollback(tm);
527   }
528
529   /**
530    * Log the beginning of a new transaction.
531    *
532    * @param tm The transaction marker metadata
533    * @return the identifier of the entry in the recovery log
534    */

535   public long logBegin(TransactionMarkerMetaData tm)
536   {
537     // Store the begin in the database
538
long id = incrementLogTableId();
539     loggerThread.log(new LogRequestEvent(new LogEntry(id, tm.getLogin(),
540         "begin", tm.getTransactionId(), false)));
541     return id;
542   }
543
544   /**
545    * Log a transaction commit.
546    *
547    * @param tm The transaction marker metadata
548    * @return the identifier of the entry in the recovery log
549    */

550   public long logCommit(TransactionMarkerMetaData tm)
551   {
552     long id = incrementLogTableId();
553     loggerThread.log(new LogRequestEvent(new LogEntry(id, tm.getLogin(),
554         "commit", tm.getTransactionId(), false)));
555     return id;
556   }
557
558   /**
559    * Log a log entry in the recovery log.
560    *
561    * @param logEntry the log entry to to be written in the recovery log.
562    */

563   public void logLogEntry(LogEntry logEntry)
564   {
565     loggerThread.log(new LogRequestEvent(logEntry));
566   }
567
568   /**
569    * Log a transaction savepoint removal.
570    *
571    * @param tm The transaction marker metadata
572    * @param name The name of the savepoint to log
573    * @return the identifier of the entry in the recovery log
574    */

575   public long logReleaseSavepoint(TransactionMarkerMetaData tm, String JavaDoc name)
576   {
577     long id = incrementLogTableId();
578     loggerThread.log(new LogRequestEvent(new LogEntry(id, tm.getLogin(),
579         "release " + name, tm.getTransactionId(), false)));
580     return id;
581   }
582
583   /**
584    * Log a write request.
585    *
586    * @param request The write request to log
587    * @return the identifier of the entry in the recovery log
588    */

589   public long logRequest(AbstractWriteRequest request)
590   {
591     long id = incrementLogTableId();
592     loggerThread.log(new LogRequestEvent(new LogEntry(id, request.getLogin(),
593         request.getSQL(), request.getTransactionId(), request
594             .getEscapeProcessing())));
595     return id;
596   }
597
598   /**
599    * Log a call to a stored procedure.
600    *
601    * @param proc The stored procedure call to log
602    * @param isRead True if the stored procedure call returns a ResultSet
603    * @return the identifier of the entry in the recovery log
604    */

605   public long logRequest(StoredProcedure proc, boolean isRead)
606   {
607     long id = incrementLogTableId();
608     if (isRead)
609       loggerThread.log(new LogRequestEvent(new LogEntry(incrementLogTableId(),
610           proc.getLogin(), proc.getSQL(), proc.getTransactionId(), proc
611               .getEscapeProcessing())));
612     else
613     { // Reverse the first bracket so that we can identify a write call
614
StringBuffer JavaDoc writeCall = new StringBuffer JavaDoc(proc.getSQL());
615       writeCall.setCharAt(0, '}');
616       loggerThread.log(new LogRequestEvent(new LogEntry(incrementLogTableId(),
617           proc.getLogin(), writeCall.toString(), proc.getTransactionId(), proc
618               .getEscapeProcessing())));
619     }
620     return id;
621   }
622
623   /**
624    * Log a transaction rollback.
625    *
626    * @param tm The transaction marker metadata
627    * @return the identifier of the entry in the recovery log
628    */

629   public long logRollback(TransactionMarkerMetaData tm)
630   {
631     long id = incrementLogTableId();
632     // Some backends started a recovery process, log the rollback
633
loggerThread.log(new LogRollbackEvent(new LogEntry(id, tm.getLogin(),
634         "rollback", tm.getTransactionId(), false)));
635     return id;
636   }
637
638   /**
639    * Log a transaction rollback to a savepoint
640    *
641    * @param tm The transaxtion marker metadata
642    * @param savepointName The name of the savepoint
643    * @return the identifier of the entry in the recovery log
644    */

645   public long logRollback(TransactionMarkerMetaData tm, String JavaDoc savepointName)
646   {
647     long id = incrementLogTableId();
648     loggerThread.log(new LogRequestEvent(new LogEntry(id, tm.getLogin(),
649         "rollback " + savepointName, tm.getTransactionId(), false)));
650     return id;
651   }
652
653   /**
654    * Log a transaction savepoint.
655    *
656    * @param tm The transaction marker metadata
657    * @param name The name of the savepoint to log
658    * @return the identifier of the entry in the recovery log
659    */

660   public long logSetSavepoint(TransactionMarkerMetaData tm, String JavaDoc name)
661   {
662     long id = incrementLogTableId();
663     loggerThread.log(new LogRequestEvent(new LogEntry(id, tm.getLogin(),
664         "savepoint " + name, tm.getTransactionId(), false)));
665     return id;
666   }
667
668   /**
669    * Reset the current log table id and delete the recovery log information
670    * older than the given checkpoint. This method also deletes all entries in
671    * the checkpoint table. This method is asynchronous: the delete is performed
672    * via a post to the loggger thread.
673    *
674    * @param checkpointName the checkpoint name to delete from.
675    * @param newCheckpointId the new checkpoint identifier
676    * @throws SQLException if an error occurs
677    */

678   public void resetLogTableIdAndDeleteRecoveryLog(String JavaDoc checkpointName,
679       long newCheckpointId) throws SQLException JavaDoc
680   {
681     long oldId = getCheckpointRequestId(checkpointName);
682     synchronized (this)
683     {
684       // resetLog cleans the recovery log table, resets the checkpointTable and
685
// renumber the queries since oldId (checkpoint assigned to the transfer).
686
loggerThread
687           .log(new ResetLogEvent(oldId, newCheckpointId, checkpointName));
688       logTableId = newCheckpointId + logTableId - oldId;
689     }
690   }
691
692   /**
693    * Remove a transaction commit from the recovery log. This commit was logged
694    * because no backend was available locally to execute it but that finally
695    * ended up in failing at all other controllers.
696    *
697    * @param tm the commited transaction
698    * @see org.objectweb.cjdbc.controller.requestmanager.distributed.DistributedRequestManager#removeFailedCommitFromRecoveryLog(TransactionMarkerMetaData)
699    */

700   public void unlogCommit(TransactionMarkerMetaData tm)
701   {
702     // Timeout is used here to transport the log id of the commit
703
loggerThread.log(new UnlogRequestEvent(new LogEntry(tm.getTimeout(), tm
704         .getLogin(), "commit", tm.getTransactionId(), false)));
705   }
706
707   /**
708    * Remove a request from the recovery log. This request was logged because no
709    * backend was available locally to execute it but that finally ended up in
710    * failing at all other controllers.
711    *
712    * @param request request that was logged but failed at all controllers and
713    * must be removed from recovery log
714    * @see org.objectweb.cjdbc.controller.requestmanager.distributed.DistributedRequestManager#removeFailedRequestFromRecoveryLog(AbstractWriteRequest,
715    * long)
716    */

717   public void unlogRequest(AbstractRequest request)
718   {
719     loggerThread.log(new UnlogRequestEvent(new LogEntry(request.getId(),
720         request.getLogin(), request.getSQL(), request.getTransactionId(),
721         request.getEscapeProcessing())));
722   }
723
724   /**
725    * Remove a stored procedure from the recovery log. This stored procedure was
726    * logged because no backend was available locally to execute it but that
727    * finally ended up in failing at all other controllers.
728    *
729    * @param proc stored procedure that was logged but failed at all controllers
730    * and must be removed from recovery log
731    * @see org.objectweb.cjdbc.controller.requestmanager.distributed.DistributedRequestManager#removeFailedStoredProcedureFromRecoveryLog(StoredProcedure)
732    */

733   public void unlogRequest(StoredProcedure proc)
734   {
735     // If we have to unlog this can only be a write stored procedure, a read
736
// would not have been broadcasted.
737
StringBuffer JavaDoc writeCall = new StringBuffer JavaDoc(proc.getSQL());
738     writeCall.setCharAt(0, '}');
739     loggerThread.log(new UnlogRequestEvent(new LogEntry(proc.getId(), proc
740         .getLogin(), writeCall.toString(), proc.getTransactionId(), proc
741         .getEscapeProcessing())));
742   }
743
744   /**
745    * Remove a transaction rollback from the recovery log. This rollback was
746    * logged because no backend was available locally to execute it but that
747    * finally ended up in failing at all other controllers.
748    *
749    * @param tm the commited transaction
750    * @see org.objectweb.cjdbc.controller.requestmanager.distributed.DistributedRequestManager#removeFailedRollbackFromRecoveryLog(TransactionMarkerMetaData)
751    */

752   public void unlogRollback(TransactionMarkerMetaData tm)
753   {
754     // Timeout is used here to transport the log id of the rollback
755
loggerThread.log(new UnlogRequestEvent(new LogEntry(tm.getTimeout(), tm
756         .getLogin(), "rollback", tm.getTransactionId(), false)));
757   }
758
759   /**
760    * Shutdown the recovery log and all its threads.
761    */

762   public void shutdown()
763   {
764     if (loggerThread != null)
765       loggerThread.shutdown();
766   }
767
768   //
769
// Recovery process
770
//
771

772   /**
773    * Notify the recovery log that a recovery process has started.
774    */

775   public synchronized void beginRecovery()
776   {
777     recoveringNb++;
778   }
779
780   /**
781    * Possibly clean the recovery log after all recovery process are done. This
782    * removes all rollbacked transaction from the recovery log.
783    *
784    * @exception SQLException if an error occurs.
785    */

786   public void cleanRecoveryLog() throws SQLException JavaDoc
787   {
788     PreparedStatement JavaDoc stmt = null;
789
790     // Remove the rollback statements and associated requests from the database
791
ResultSet JavaDoc rs = null;
792     try
793     {
794       // Get the list of transaction ids on which a rollback occurred
795
stmt = getDatabaseConnection().prepareStatement(
796           "SELECT transaction_id FROM " + logTableName + " WHERE "
797               + logTableSqlColumnName + " LIKE ?");
798       stmt.setString(1, "rollback");
799       rs = stmt.executeQuery();
800     }
801     catch (SQLException JavaDoc e)
802     {
803       invalidateInternalConnection();
804       try
805       {
806         if (stmt != null)
807           stmt.close();
808       }
809       catch (Exception JavaDoc ignore)
810       {
811       }
812       throw new SQLException JavaDoc("Unable get rollback statements : " + e);
813     }
814     PreparedStatement JavaDoc pstmt = null;
815     long transactionId = -1;
816     try
817     {
818       // remove the rollbacked transaction from the database
819
while (rs.next())
820       {
821         transactionId = rs.getLong("transaction_id");
822         pstmt = getDatabaseConnection().prepareStatement(
823             "DELETE FROM " + logTableName + " WHERE transaction_id=?");
824         pstmt.setLong(1, transactionId);
825         pstmt.executeUpdate();
826         pstmt.close();
827       }
828       rs.close();
829       stmt.close();
830     }
831     catch (SQLException JavaDoc e)
832     {
833       invalidateInternalConnection();
834       throw new SQLException JavaDoc(Translate.get(
835           "recovery.jdbc.transaction.remove.failed", new String JavaDoc[]{
836               String.valueOf(transactionId), e.getMessage()}));
837     }
838     finally
839     {
840       try
841       {
842         if (stmt != null)
843           stmt.close();
844       }
845       catch (Exception JavaDoc ignore)
846       {
847       }
848       try
849       {
850         if (pstmt != null)
851           pstmt.close();
852       }
853       catch (Exception JavaDoc ignore)
854       {
855       }
856
857     }
858