00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
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
00061
00062
00063
00064
00065
00066
00067 public class JDBCRecoveryLog extends AbstractRecoveryLog
00068 implements
00069 AbstractRecoveryLogMBean
00070 {
00071
00072 private String driverClassName;
00073
00074
00075 private String driverName;
00076
00077
00078 private String url;
00079
00080
00081 private String login;
00082
00083
00084 private String password;
00085
00086
00087 private String logTableName;
00088 private String logTableCreateStatement;
00089
00090 private String idType;
00091 private String vloginType;
00092 private String sqlType;
00093 private String transactionIdType;
00094 private String logextraStatement;
00095
00096
00097 private String checkpointTableName;
00098 private String checkpointTableCreateStatement;
00099
00100
00101
00102
00103
00104
00105
00106 private String backendTableCreateStatement;
00107 private String backendTableName;
00108
00109
00110 private String nameType;
00111 private String requestIdType;
00112 private String checkextraStatement;
00113
00114
00115 private Connection connection;
00116
00117
00118 private PreparedStatement pstmt;
00119
00120
00121 private long logTableId = 0;
00122
00123
00124 private int timeout;
00125
00126 private JDBCLoggerThread loggerThread;
00127
00128 private String sqlColumnName;
00129
00130
00131
00132
00133
00134
00135
00136
00137
00138
00139
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
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
00164
00165 }
00166
00167
00168
00169
00170
00171 private void intializeDatabase() throws SQLException
00172 {
00173 boolean createLogTable = true;
00174 boolean createCheckpointTable = true;
00175 boolean createBackendTable = true;
00176
00177 try
00178 {
00179 connection.setAutoCommit(false);
00180
00181 DatabaseMetaData metaData = connection.getMetaData();
00182
00183
00184
00185
00186
00187
00188
00189 String[] types = {"TABLE", "VIEW"};
00190 ResultSet rs = metaData.getTables(null, null, "%", types);
00191
00192
00193 String tableName;
00194 while (rs.next())
00195 {
00196
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
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
00260 }
00261 }
00262 catch (SQLException e)
00263 {
00264 logger.error(Translate.get("recovery.jdbc.table.no.description"), e);
00265 throw e;
00266 }
00267
00268
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
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
00358
00359
00360
00361 private void connectToDatabase() throws SQLException
00362 {
00363
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
00389
00390
00391
00392
00393
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
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 {
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
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
00462
00463 private synchronized long incrementLogTableId()
00464 {
00465 logTableId++;
00466 return logTableId;
00467 }
00468
00469
00470
00471
00472 public void begin(TransactionMarkerMetaData tm) throws SQLException
00473 {
00474
00475 loggerThread.log(incrementLogTableId(), tm.getLogin(), "begin", tm
00476 .getTransactionId(), false);
00477 }
00478
00479
00480
00481
00482 public void abort(TransactionMarkerMetaData tm) throws SQLException
00483 {
00484
00485 rollback(tm);
00486 }
00487
00488
00489
00490
00491 public void commit(TransactionMarkerMetaData tm) throws SQLException
00492 {
00493
00494 loggerThread.log(incrementLogTableId(), tm.getLogin(), "commit", tm
00495 .getTransactionId(), false);
00496 }
00497
00498
00499
00500
00501 public void rollback(TransactionMarkerMetaData tm) throws SQLException
00502 {
00503 long transactionId = tm.getTransactionId();
00504 if (isRecovering())
00505 {
00506
00507 loggerThread.log(incrementLogTableId(), tm.getLogin(), "rollback",
00508 transactionId, false);
00509 }
00510 else
00511 {
00512
00513
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
00546
00547
00548
00549
00550
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
00604
00605 public RecoveryTask recoverNextRequest(long previousRequestId)
00606 throws SQLException
00607 {
00608 RecoveryTask task = null;
00609
00610
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
00620 do
00621 {
00622
00623
00624 stmt.setLong(1, previousRequestId);
00625 stmt.setLong(2, previousRequestId + 2);
00626
00627 if (rs != null)
00628 rs.close();
00629 rs = stmt.executeQuery();
00630 previousRequestId += 2;
00631 emptyResult = !rs.next();
00632 }
00633 while (emptyResult && (previousRequestId <= logTableId));
00634
00635
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
00649 boolean escapeProcessing = true;
00650 sql = sql.trim();
00651
00652
00653
00654 String lower = sql.substring(0, 2).toLowerCase();
00655 if (lower.equals("in"))
00656 {
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 {
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 {
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 {
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 {
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 {
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 {
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 {
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 {
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 {
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 {
00804
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
00846
00847
00848
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;
00858
00859 request.setSqlSkeleton(recreateSkeleton(sql));
00860 }
00861
00862
00863
00864
00865
00866
00867
00868 private String recreateSkeleton(String sql)
00869 {
00870
00871
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
00893
00894
00895
00896 public void cleanRecoveryLog() throws SQLException
00897 {
00898 PreparedStatement stmt = null;
00899
00900
00901 ResultSet rs = null;
00902 try
00903 {
00904
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
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
00970
00971
00972
00973
00974
00975
00976 public ArrayList getCheckpointNames() throws SQLException
00977 {
00978 PreparedStatement stmt = null;
00979
00980
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
01018
01019
01020
01021
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
01036
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
01061
01062 public void storeCheckpoint(String checkpointName) throws SQLException
01063 {
01064 storeCheckpoint(checkpointName, logTableId);
01065 }
01066
01067
01068
01069
01070
01071 public void storeCheckpoint(String checkpointName, long requestId)
01072 throws SQLException
01073 {
01074 PreparedStatement stmt = null;
01075
01076
01077 if (!validCheckpointName(checkpointName))
01078 {
01079 throw new SQLException(Translate.get(
01080 "recovery.jdbc.checkpoint.duplicate", checkpointName));
01081 }
01082
01083
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
01118
01119 public void removeCheckpoint(String checkpointName) throws SQLException
01120 {
01121 PreparedStatement stmt = null;
01122
01123
01124 waitForTransactionsEnd(true);
01125
01126 try
01127 {
01128
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
01147 stmt = connection.prepareStatement("DELETE FROM " + logTableName
01148 + " WHERE ID <= ?");
01149 stmt.setInt(1, requestId);
01150 stmt.executeUpdate();
01151 stmt.close();
01152
01153
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
01183
01184
01185
01186
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
01216
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
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
01262
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("");
01273 else
01274 {
01275 getCheckpointRequestId(backendRecoveryInfo.getCheckpoint());
01276 }
01277
01278 try
01279 {
01280
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
01345
01346
01347
01348
01349
01350
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
01366 loggerThread = new JDBCLoggerThread(pstmt, logger);
01367 loggerThread.start();
01368 }
01369
01370
01371
01372
01373
01374
01375 public String getBackendTableName()
01376 {
01377 return backendTableName;
01378 }
01379
01380
01381
01382
01383
01384
01385 public String getCheckpointTableName()
01386 {
01387 return checkpointTableName;
01388 }
01389
01390
01391
01392
01393
01394
01395 public String getLogTableName()
01396 {
01397 return logTableName;
01398 }
01399
01400
01401
01402
01403
01404
01405
01406
01407
01408
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
01427
01428
01429
01430
01431
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
01441
01442
01443
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
01457
01458
01459
01460
01461
01462
01463
01464
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
01491
01492
01493
01494
01495
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
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
01542
01543 public String getAssociatedString()
01544 {
01545 return "jdbcrecoverylog";
01546 }
01547
01548
01549
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
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
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
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 }