001: /*
002:
003: Derby - Class org.apache.derbyTesting.functionTests.store.OnlineBackupTest3
004:
005: Licensed to the Apache Software Foundation (ASF) under one or more
006: contributor license agreements. See the NOTICE file distributed with
007: this work for additional information regarding copyright ownership.
008: The ASF licenses this file to You under the Apache License, Version 2.0
009: (the "License"); you may not use this file except in compliance with
010: the License. You may obtain a copy of the License at
011:
012: http://www.apache.org/licenses/LICENSE-2.0
013:
014: Unless required by applicable law or agreed to in writing, software
015: distributed under the License is distributed on an "AS IS" BASIS,
016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: See the License for the specific language governing permissions and
018: limitations under the License.
019:
020: */
021:
022: package org.apache.derbyTesting.functionTests.tests.store;
023:
024: import java.sql.Connection;
025: import java.sql.Statement;
026: import java.sql.PreparedStatement;
027: import java.sql.ResultSet;
028: import java.sql.SQLException;
029: import org.apache.derby.tools.ij;
030: import org.apache.derbyTesting.functionTests.util.TestUtil;
031:
032: /*
033: * This class tests online backup when jar actions
034: * are running in parallel to the backup thread.
035: *
036: * @author <a href="mailto:suresh.thalamati@gmail.com">Suresh Thalamati</a>
037: * @version 1.0
038: */
039:
040: public class OnlineBackupTest3 {
041:
042: private static final String TEST_DATABASE_NAME = "wombat";
043: private static final String BACKUP_PATH = "extinout/onlinebackuptest3";
044:
045: public static void main(String[] argv) throws Throwable {
046:
047: OnlineBackupTest3 test = new OnlineBackupTest3();
048: ij.getPropertyArg(argv);
049:
050: try {
051: test.runTest();
052: } catch (SQLException sqle) {
053: dumpSQLException(sqle);
054: }
055: }
056:
057: /*
058: * Test online backup with unlogged jar operations running in parallel.
059: */
060: private void runTest() throws Exception {
061: logMessage("Begin Online Backup Test3");
062: Connection conn = ij.startJBMS();
063: conn.setAutoCommit(false);
064: Statement stmt = conn.createStatement();
065: stmt.execute("create table t1(a int ) ");
066: stmt.execute("insert into t1 values(1)");
067: stmt.execute("insert into t1 values(2)");
068: stmt
069: .execute("create table customer(id int , name varchar(100))");
070: stmt.execute("insert into customer values(1, 'ABC')");
071: stmt.execute("insert into customer values(2, 'XYZ')");
072: String crproc = "create procedure addCustomer(id INT, name VARCHAR(100)) "
073: + "MODIFIES SQL DATA "
074: + "external name "
075: + "'org.apache.derbyTesting.backupRestore.Customer.addCustomer' "
076: + " language java parameter style java ";
077:
078: stmt.execute(crproc);
079:
080: String dvfunc = "create function dv(P1 INT) RETURNS INT NO SQL "
081: + " external name 'dbytesting.CodeInAJar.doubleMe' "
082: + " language java parameter style java ";
083:
084: stmt.execute(dvfunc);
085: conn.commit();
086:
087: logMessage("Initial Setup Complete");
088:
089: // perform install jar operation with
090: // online backup running in parallel.
091: installJarTest();
092:
093: // perform remove jar operation with
094: // online backup running in parallel.
095: removeJarTest();
096:
097: logMessage("End Online Backup Test3");
098: }
099:
100: /**
101: * Shutdown the datbase
102: * @param dbName Name of the database to shutdown.
103: */
104: void shutdown(String dbName) {
105:
106: try {
107: //shutdown
108: TestUtil.getConnection(dbName, "shutdown=true");
109: } catch (SQLException se) {
110: if (se.getSQLState() != null
111: && se.getSQLState().equals("08006"))
112: System.out.println("database shutdown properly");
113: else
114: dumpSQLException(se);
115: }
116: }
117:
118: /**
119: * Write message to the standard output.
120: */
121: void logMessage(String str) {
122: System.out.println(str);
123: }
124:
125: /**
126: * dump the SQLException to the standard output.
127: */
128: static private void dumpSQLException(SQLException sqle) {
129:
130: org.apache.derby.tools.JDBCDisplayUtil.ShowSQLException(
131: System.out, sqle);
132: sqle.printStackTrace(System.out);
133: }
134:
135: private int countRows(Connection conn, String tableName)
136: throws SQLException {
137: Statement s = conn.createStatement();
138: ResultSet rs = s.executeQuery("SELECT count(*) from "
139: + tableName);
140: rs.next();
141: int noRows = rs.getInt(1);
142: rs.close();
143: s.close();
144: return noRows;
145: }
146:
147: /*
148: * Test install jar running in parallel to backup and vice versa.
149: */
150: void installJarTest() throws Exception {
151: logMessage("Begin Install Jar Test");
152: Connection conn1 = TestUtil.getConnection(TEST_DATABASE_NAME,
153: null);
154: conn1.setAutoCommit(false);
155: Statement conn1_stmt = conn1.createStatement();
156: Connection conn2 = TestUtil.getConnection(TEST_DATABASE_NAME,
157: null);
158: conn2.setAutoCommit(false);
159: Statement conn2_stmt = conn2.createStatement();
160:
161: conn1_stmt
162: .execute("call sqlj.install_jar('extin/brtestjar.jar', 'math_routines', 0)");
163:
164: try {
165: // followng backup call should fail because jar operation is pending
166: conn2_stmt
167: .execute("call SYSCS_UTIL.SYSCS_BACKUP_DATABASE_NOWAIT('extinout/mybackup')");
168: } catch (SQLException sqle) {
169: //above statement should have failed.
170: org.apache.derby.tools.JDBCDisplayUtil.ShowSQLException(
171: System.out, sqle);
172: }
173:
174: // invoke backup in another thread, it should block for the above install jar
175: // operation to install 'brtestjar.jar to commit.
176:
177: // start a thread to perform online backup
178: OnlineBackup backup = new OnlineBackup(TEST_DATABASE_NAME,
179: BACKUP_PATH);
180: Thread backupThread = new Thread(backup, "BACKUP1");
181: backupThread.start();
182: // wait for the backup to start
183: backup.waitForBackupToBegin();
184: logMessage("Backup-1 Started");
185:
186: // sleep for few seconds just to make sure backup thread has actually
187: // gone into a wait state for unlogged actions to commit.
188: java.lang.Thread.sleep(1000);
189:
190: // backup should not even start doing real work before the
191: // unlogged transaction is commited
192: if (!backup.isRunning())
193: logMessage("Backup is not waiting for unlogged "
194: + "install jar action to commit");
195:
196: //insert some rows that should appear in the backup.
197: conn1_stmt.execute("insert into t1 values(3)");
198: conn1_stmt.execute("insert into t1 values(4)");
199: conn1_stmt.execute("insert into t1 values(5)");
200:
201: // set the database class with both the jars installed above.
202: conn1_stmt
203: .execute("CALL SYSCS_UTIL.SYSCS_SET_DATABASE_PROPERTY( "
204: + "'derby.database.classpath', "
205: + "'APP.math_routines') ");
206:
207: //commit the transaction with jar opearation that is blocking the backup.
208: conn1.commit();
209: logMessage("The transaction that was blocking the backup has ended");
210:
211: // wait for backup to finish.
212: backup.waitForBackupToEnd();
213: backupThread.join();
214: logMessage("Backup-1 Completed");
215:
216: // Case : jar op should block if backup is in progress
217: // add a index that will block the backup until it is committted.
218: conn1_stmt.execute("create index idx1 on customer(id)");
219: conn1_stmt.execute("insert into t1 values(6)");
220:
221: // start a thread to perform online backup
222: backup = new OnlineBackup(TEST_DATABASE_NAME, BACKUP_PATH);
223: backupThread = new Thread(backup, "BACKUP2");
224: backupThread.start();
225: // wait for the backup to start
226: backup.waitForBackupToBegin();
227: logMessage("Backup-2 Started");
228:
229: // sleep for few seconds just to make sure backup thread is actually
230: // gone to a wait state for unlogged actions to commit.
231: java.lang.Thread.sleep(1000);
232:
233: // backup should not even start doing real work before the
234: // unlogged transaction is commited
235: if (!backup.isRunning())
236: logMessage("Backup is not waiting for unlogged "
237: + "index action to commit");
238:
239: // add another jar file , this one should block and
240: // should not get into the backup. Backup does not allow new
241: // jar operation if it is already waiting for backup blocking
242: // to complete(commit/rollback).
243:
244: AsyncStatementThread asyncJarActionThread = new AsyncStatementThread(
245: conn2,
246: "call sqlj.install_jar('extin/obtest_customer.jar', 'customer_app', 0)");
247: asyncJarActionThread.start();
248: logMessage("Started obtest_customer.jar addition in seperate thread");
249:
250: //sleep for few seconds to give a chance for the
251: //jar addition thread to get into action.
252: java.lang.Thread.sleep(1000);
253:
254: //roll back the index op. Backup should proceed now.
255: conn1.rollback();
256: logMessage("The transaction that was blocking the backup has ended");
257:
258: // wait for backup to finish.
259: backup.waitForBackupToEnd();
260: backupThread.join();
261: logMessage("Backup-2 Completed");
262:
263: // wait for customer app jar installation to finish now.
264: asyncJarActionThread.join();
265: logMessage("obtest_customer.jar addition is complete");
266:
267: // set the database class with both the jars installed above.
268: conn1_stmt
269: .execute("CALL SYSCS_UTIL.SYSCS_SET_DATABASE_PROPERTY( "
270: + "'derby.database.classpath', "
271: + "'APP.customer_app:APP.math_routines') ");
272:
273: conn1.commit();
274:
275: // second jar must have got installed after the backup.
276: // call a function in the custome_app jar
277: conn1_stmt.execute("call addCustomer(3 , 'John')");
278: conn1.commit();
279:
280: logMessage("No of rows in table t1: " + countRows(conn1, "T1"));
281: logMessage("No of rows in table customer: "
282: + countRows(conn1, "customer"));
283: conn1.commit();
284: conn2.commit();
285: conn1_stmt.close();
286: conn2_stmt.close();
287: conn1.close();
288: conn2.close();
289:
290: //shutdown the test db
291: shutdown(TEST_DATABASE_NAME);
292: // restore the database from the backup and run some checks
293: backup.restoreFromBackup();
294: logMessage("Restored From the Backup");
295: Connection conn = TestUtil.getConnection(TEST_DATABASE_NAME,
296: null);
297: Statement stmt = conn.createStatement();
298: logMessage("No of rows in table t1: " + countRows(conn, "T1"));
299: logMessage("No of rows in table customer: "
300: + countRows(conn, "customer"));
301: // execute select statement using the "dv" funciont.
302: stmt.execute("select dv(a) from t1");
303:
304: try {
305: // set the database class with both the jars installed above.
306: stmt
307: .execute("CALL SYSCS_UTIL.SYSCS_SET_DATABASE_PROPERTY( "
308: + "'derby.database.classpath', "
309: + "'APP.customer_app:APP.math_routines') ");
310: stmt.execute("call addCustomer(3 , 'John')");
311: } catch (SQLException se) {
312: //ignore for now. No sure way to
313: //check that jar did not get into backup
314: //without debug flags.
315: }
316:
317: stmt.close();
318: conn.close();
319:
320: //shutdown the test db
321: shutdown(TEST_DATABASE_NAME);
322: logMessage("End Of Install Jar Test.");
323:
324: }
325:
326: /*
327: * Test remove jar running in parallel to backup and vice versa.
328: */
329: void removeJarTest() throws Exception {
330: logMessage("Begin Remove Jar Test");
331: Connection conn1 = TestUtil.getConnection(TEST_DATABASE_NAME,
332: null);
333: conn1.setAutoCommit(false);
334: Statement conn1_stmt = conn1.createStatement();
335: Connection conn2 = TestUtil.getConnection(TEST_DATABASE_NAME,
336: null);
337: conn2.setAutoCommit(false);
338: Statement conn2_stmt = conn2.createStatement();
339: try {
340: conn1_stmt
341: .execute("call sqlj.install_jar('extin/obtest_customer.jar', 'customer_app', 0)");
342: } catch (SQLException se) {
343: //it is ok if was jar already there.
344: }
345:
346: // remove both the jars from the class path ,
347: // so that we can remove them from the database.
348: conn1_stmt
349: .execute("CALL SYSCS_UTIL.SYSCS_SET_DATABASE_PROPERTY( "
350: + "'derby.database.classpath', '')");
351: conn1.commit();
352:
353: conn1_stmt
354: .execute("call sqlj.remove_jar('APP.math_routines', 0)");
355:
356: // Case 0: backup call that is not waiting for unlogged
357: // opereation to complete should fail when a remove jar
358: // is not ended when backup started.
359:
360: try {
361: // followng backup call should fail because remove
362: // jar operation is pending
363: conn2_stmt
364: .execute("call SYSCS_UTIL.SYSCS_BACKUP_DATABASE_NOWAIT('extinout/mybackup')");
365: } catch (SQLException sqle) {
366: //above statement should have failed.
367: org.apache.derby.tools.JDBCDisplayUtil.ShowSQLException(
368: System.out, sqle);
369: }
370:
371: // Case 1: backup should block because when a remove jar
372: // is not ended when backup started.
373:
374: // invoke backup in another thread, should block for
375: // the above remove jar to commit.
376:
377: // start a thread to perform online backup
378: OnlineBackup backup = new OnlineBackup(TEST_DATABASE_NAME,
379: BACKUP_PATH);
380: Thread backupThread = new Thread(backup, "BACKUP3");
381: backupThread.start();
382: // wait for the backup to start
383: backup.waitForBackupToBegin();
384: logMessage("Backup-3 Started");
385:
386: // sleep for few seconds just to make sure backup thread is actually
387: // gone to a wait state for unlogged actions to commit.
388: java.lang.Thread.sleep(1000);
389:
390: // backup should not even start doing real work before the
391: // unlogged transaction is commited
392: if (!backup.isRunning())
393: logMessage("Backup is not waiting for unlogged "
394: + "remove jar action to commit");
395:
396: //insert some rows that should appear in the backup.
397: conn1_stmt.execute("insert into t1 values(10)");
398: conn1_stmt.execute("insert into t1 values(11)");
399:
400: //commit the transaction with jar opearation that is blocking the backup.
401: conn1.commit();
402: logMessage("The transaction that was blocking the backup has ended");
403:
404: // wait for backup to finish.
405: backup.waitForBackupToEnd();
406: backupThread.join();
407:
408: logMessage("Backup-3 Completed");
409:
410: // Case 2: remove jar op should block if backup is in progress
411: // add a index that will block the backup until it is committted.
412: conn1_stmt.execute("create index idx1 on customer(id)");
413: conn1_stmt.execute("insert into t1 values(12)");
414:
415: // start a thread to perform online backup
416: backup = new OnlineBackup(TEST_DATABASE_NAME, BACKUP_PATH);
417: backupThread = new Thread(backup, "BACKUP4");
418: backupThread.start();
419: // wait for the backup to start
420: backup.waitForBackupToBegin();
421: logMessage("Backup-4 Started");
422:
423: // sleep for few seconds just to make sure backup thread is actually
424: // gone to a wait state for unlogged actions to commit.
425: java.lang.Thread.sleep(1000);
426:
427: // backup should not even start doing real work before the
428: // unlogged transaction is commited
429: if (!backup.isRunning())
430: logMessage("Backup is not waiting for unlogged "
431: + "index action to commit");
432:
433: // remove another jar file , this one should block and
434: // should not get into the backup. Backup does not allow new
435: // jar operation if it is already waiting for backup blocking
436: // to complete(commit/rollback).
437:
438: AsyncStatementThread asyncJarActionThread = new AsyncStatementThread(
439: conn2, "call sqlj.remove_jar('APP.customer_app', 0)");
440: asyncJarActionThread.start();
441: logMessage("Started obtest_customer.jar remove in seperate thread");
442:
443: //sleep for few seconds to give a chance for the
444: //jar addition thread to get into action.
445: java.lang.Thread.sleep(1000);
446:
447: //roll back the index op. Backup should proceed now.
448: conn1.rollback();
449: logMessage("The transaction that was blocking the backup has ended");
450: // wait for backup to finish.
451: backup.waitForBackupToEnd();
452: backupThread.join();
453: logMessage("Backup-4 Completed");
454:
455: // wait for customer app jar removal to finish now.
456: asyncJarActionThread.join();
457: logMessage("obtest_customer.jar remove is complete");
458:
459: //this insert should not apprear on restore.
460: conn1_stmt.execute("insert into t1 values(13)");
461:
462: logMessage("No of rows in table t1: " + countRows(conn1, "T1"));
463: logMessage("No of rows in table customer: "
464: + countRows(conn1, "customer"));
465: conn1.commit();
466: conn2.commit();
467: conn1_stmt.close();
468: conn2_stmt.close();
469: conn1.close();
470: conn2.close();
471:
472: //shutdown the test db
473: shutdown(TEST_DATABASE_NAME);
474: // restore the database from the backup and run some checks
475: backup.restoreFromBackup();
476: logMessage("Restored From the Backup");
477: Connection conn = TestUtil.getConnection(TEST_DATABASE_NAME,
478: null);
479: Statement stmt = conn.createStatement();
480: logMessage("No of rows in table t1: " + countRows(conn, "T1"));
481: logMessage("No of rows in table customer: "
482: + countRows(conn, "customer"));
483:
484: // check if the jar removal was successful.
485: // APP.math_routines should not be in backup.
486: try {
487: // set the database class path with the jar removed above,
488: // it should fail.
489: stmt
490: .execute("CALL SYSCS_UTIL.SYSCS_SET_DATABASE_PROPERTY( "
491: + "'derby.database.classpath', "
492: + "'APP.math_routines') ");
493: } catch (SQLException sqle) {
494: //above statement should have failed.
495: org.apache.derby.tools.JDBCDisplayUtil.ShowSQLException(
496: System.out, sqle);
497: }
498:
499: stmt.close();
500: conn.close();
501:
502: //shutdown the test db
503: shutdown(TEST_DATABASE_NAME);
504: logMessage("End Of Remove Jar Test.");
505:
506: }
507:
508: /*
509: * Run a sql statement in a seperate thread.
510: */
511: class AsyncStatementThread extends Thread {
512: Connection conn;
513: String stmt;
514:
515: AsyncStatementThread(Connection conn, String stmt) {
516: this .conn = conn;
517: this .stmt = stmt;
518: }
519:
520: public void run() {
521: Statement aStatement = null;
522: try {
523: aStatement = conn.createStatement();
524: aStatement.execute(stmt);
525: aStatement.close();
526: // commit here, it is possible that
527: // this thread may have got into action
528: // before the backup went into wait state.
529: conn.commit();
530: } catch (SQLException sqle) {
531: org.apache.derby.tools.JDBCDisplayUtil
532: .ShowSQLException(System.out, sqle);
533: sqle.printStackTrace(System.out);
534: }
535: aStatement = null;
536: }
537: }
538:
539: }
|