KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > derbyTesting > functionTests > tests > store > ReEncryptCrashRecovery


1 /*
2
3    Derby - Class org.apache.derbyTesting.functionTests.store.ReEncryptCrashRecovery
4
5    Licensed to the Apache Software Foundation (ASF) under one or more
6    contributor license agreements. See the NOTICE file distributed with
7    this work for additional information regarding copyright ownership.
8    The ASF licenses this file to You under the Apache License, Version 2.0
9    (the "License"); you may not use this file except in compliance with
10    the License. You may obtain a copy of the License at
11
12       http://www.apache.org/licenses/LICENSE-2.0
13
14    Unless required by applicable law or agreed to in writing, software
15    distributed under the License is distributed on an "AS IS" BASIS,
16    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17    See the License for the specific language governing permissions and
18    limitations under the License.
19
20  */

21
22 package org.apache.derbyTesting.functionTests.tests.store;
23 import java.sql.Connection JavaDoc;
24 import java.sql.Statement JavaDoc;
25 import java.sql.PreparedStatement JavaDoc;
26 import java.sql.ResultSet JavaDoc;
27 import java.sql.SQLException JavaDoc;
28 import org.apache.derby.tools.ij;
29 import org.apache.derbyTesting.functionTests.util.TestUtil;
30 import org.apache.derby.iapi.services.sanity.SanityManager;
31
32 /*
33  * This class tests crash/recovery scenarions during (re) encryption of
34  * database. Debug flags are used to simulate crashes during the
35  * encrytpion of an un-encrypted database and re-encryption of an encrypted
36  * database with new password/key.
37  *
38  * Unlike the other recovery tests which do a setup and recovery as different
39  * tests, Incase of re-encryption crash/recovery can be simulated in one
40  * test itself because re-encryption is done at boot time. When debug flags are
41  * set database boot itself fails. To test the recovery, it is just a matter
42  * of clearing up the debug flag and rebooting the database.
43  *
44  * In Non debug mode, this tests does not do anything.
45  *
46  * @author <a HREF="mailto:suresh.thalamati@gmail.com">Suresh Thalamati</a>
47  * @version 1.0
48  */

49
50 public class ReEncryptCrashRecovery
51 {
52
53     // database name used to test re-encryption of an encrypted database
54
// using a new boot password.
55
private static final String JavaDoc TEST_REENCRYPT_PWD_DATABASE = "wombat_pwd_ren" ;
56     // database name used to test encryption and un-encrypted database.
57
// using a boot password.
58
private static final String JavaDoc TEST_ENCRYPT_PWD_DATABASE = "wombat_pwd_en";
59
60
61     // database name used to test re-encryption of an encrypted database
62
// using the external encryption key.
63
private static final String JavaDoc TEST_REENCRYPT_KEY_DATABASE = "wombat_key_ren" ;
64     // database name used to test encryption of un-encrypted database.
65
// using external encryption key.
66
private static final String JavaDoc TEST_ENCRYPT_KEY_DATABASE = "wombat_key_en";
67
68     // flags to indicate type of mechanism used to test the (re)encryption
69
private static final int USING_KEY = 1;
70     private static final int USING_PASSWORD = 2;
71
72     // flags to indicate the password/key to be used during recovery
73
// on reboot after a crash.
74
private static final int NONE = 1;
75     private static final int OLD = 2;
76     private static final int NEW = 3;
77
78     // test table name.
79
private static final String JavaDoc TEST_TABLE_NAME = "emp";
80
81     private static final String JavaDoc OLD_PASSWORD = "xyz1234abc";
82     private static final String JavaDoc NEW_PASSWORD = "new1234xyz";
83     
84     private static final String JavaDoc OLD_KEY = "6162636465666768";
85     private static final String JavaDoc NEW_KEY = "5666768616263646";
86     
87     // the current database being tested.
88
private String JavaDoc currentTestDatabase ;
89     // the current encryption type being tested.
90
private int encryptionType;
91
92     // set the following to true, for this test
93
// spit out more status messages.
94
private boolean verbose = false;
95
96
97     ReEncryptCrashRecovery() {
98         
99     }
100
101
102     /*
103      * Test (re)encrytpion crash/recovery scenarios.
104      */

105     private void runTest() throws Exception JavaDoc {
106         logMessage("Begin ReEncryptCrashRecovery Test");
107
108         if (SanityManager.DEBUG) {
109             if (verbose)
110                 logMessage("Start testing re-encryption with Password");
111             // test crash recovery during re-encryption
112
// using the password mechanism.
113
currentTestDatabase = TEST_REENCRYPT_PWD_DATABASE;
114             encryptionType = USING_PASSWORD;
115             runCrashRecoveryTestCases(true);
116
117         
118             if (verbose)
119             logMessage("Start Testing encryption with Password");
120
121             // test crash recovery during databse encryption
122
// using the password mechanism.
123
currentTestDatabase = TEST_ENCRYPT_PWD_DATABASE;
124             encryptionType = USING_PASSWORD;
125             // run crash recovery test cases.
126
runCrashRecoveryTestCases(false);
127
128
129             if (verbose) {
130                 logMessage("Start Testing Encryption with external Key");
131             }
132             // test crash recovery during database encryption
133
// using the encryption key.
134

135             currentTestDatabase = TEST_ENCRYPT_KEY_DATABASE;
136             encryptionType = USING_KEY;
137             runCrashRecoveryTestCases(false);
138
139             if (verbose)
140                 logMessage("Start Testing re-encryption with external Key");
141
142             // test crash recovery dureing re-encryption
143
// using the encryption key.
144

145             currentTestDatabase = TEST_REENCRYPT_KEY_DATABASE;
146             encryptionType = USING_KEY;
147             runCrashRecoveryTestCases(true);
148         }
149         logMessage("End ReEncryptCrashRecovery Test");
150     }
151
152
153     /**
154      * run crash recovery test scenarios using the debug flags.
155      * @param reEncrypt <code> true </code> if testing re-encryption
156      * <colde> false </code> otherwise.
157      */

158     private void runCrashRecoveryTestCases(boolean reEncrypt)
159         throws SQLException JavaDoc
160     {
161         Connection JavaDoc conn;
162         if (reEncrypt)
163             conn = createEncryptedDatabase();
164         else
165             conn = createDatabase();
166
167         createTable(conn, TEST_TABLE_NAME);
168         //load some rows
169
insert(conn, TEST_TABLE_NAME, 100);
170         conn.commit();
171         conn.close();
172         shutdown();
173
174         // following cases of (re) encryption should be rolled back.
175
int passwordKey = (reEncrypt ? OLD : NONE );
176
177         crash(reEncrypt, TEST_REENCRYPT_CRASH_BEFORE_COMMT);
178
179         crash(reEncrypt, TEST_REENCRYPT_CRASH_AFTER_COMMT);
180         crashInRecovery(passwordKey,
181                      TEST_REENCRYPT_CRASH_AFTER_RECOVERY_UNDO_LOGFILE_DELETE);
182         crashInRecovery(passwordKey,
183                      TEST_REENCRYPT_CRASH_AFTER_RECOVERY_UNDO_REVERTING_KEY);
184         crashInRecovery(passwordKey,
185                      TEST_REENCRYPT_CRASH_BEFORE_RECOVERY_FINAL_CLEANUP);
186
187         
188         crash(reEncrypt, TEST_REENCRYPT_CRASH_AFTER_COMMT);
189         crashInRecovery(passwordKey,
190                      TEST_REENCRYPT_CRASH_AFTER_RECOVERY_UNDO_LOGFILE_DELETE);
191         // retry (re)encryption and crash.
192
crash(reEncrypt, TEST_REENCRYPT_CRASH_AFTER_COMMT);
193
194
195         crash(reEncrypt, TEST_REENCRYPT_CRASH_AFTER_SWITCH_TO_NEWKEY);
196         crashInRecovery(passwordKey,
197                      TEST_REENCRYPT_CRASH_AFTER_RECOVERY_UNDO_LOGFILE_DELETE);
198         crashInRecovery(passwordKey,
199                      TEST_REENCRYPT_CRASH_AFTER_RECOVERY_UNDO_REVERTING_KEY);
200         crashInRecovery(passwordKey,
201                      TEST_REENCRYPT_CRASH_BEFORE_RECOVERY_FINAL_CLEANUP);
202
203
204         crash(reEncrypt, TEST_REENCRYPT_CRASH_AFTER_SWITCH_TO_NEWKEY);
205         crashInRecovery(passwordKey,
206                      TEST_REENCRYPT_CRASH_AFTER_RECOVERY_UNDO_REVERTING_KEY);
207         // retry (re)encryption and crash.
208
crash(reEncrypt, TEST_REENCRYPT_CRASH_AFTER_SWITCH_TO_NEWKEY);
209         crashInRecovery(passwordKey,
210                      TEST_REENCRYPT_CRASH_BEFORE_RECOVERY_FINAL_CLEANUP);
211
212
213         // following cases (re) encryption should be successful, only
214
// cleanup is pending.
215

216         // crash after database is re-encrypted, but before cleanup.
217
// (re)encryption is complete, database should be bootable
218
// with a new password.
219
passwordKey = (reEncrypt ? NEW : OLD);
220         crash(reEncrypt, TEST_REENCRYPT_CRASH_AFTER_CHECKPOINT);
221         crashInRecovery(passwordKey,
222                      TEST_REENCRYPT_CRASH_BEFORE_RECOVERY_FINAL_CLEANUP);
223
224         recover(passwordKey);
225         shutdown();
226     }
227
228
229     /*
230      * Attempt to (re)encrypt the database and force it to crash
231      * at the given debug flag.
232      */

233     private void crash(boolean reEncrypt, String JavaDoc debugFlag)
234     {
235         if (verbose)
236             logMessage("Testing : " + debugFlag);
237         // set the debug flag to crash.
238
setDebugFlag(debugFlag);
239
240         SQLException JavaDoc sqle = null;
241         Connection JavaDoc conn;
242         try {
243             if (reEncrypt)
244                 conn = reEncryptDatabase();
245             else
246                 conn = encryptDatabase();
247                 
248         }catch (SQLException JavaDoc se) {
249             // (re)encryption of the database should have failed,
250
// at the specified debug flag.
251
sqle = se;
252         }
253
254         // check that database boot failed at the set debug flag.
255
verifyException(sqle, debugFlag);
256         // clear the debug flag.
257
clearDebugFlag(debugFlag);
258     }
259
260
261     /*
262      * Crash in recovery of the database at the given
263      * debug flag.
264      */

265     private void crashInRecovery(int passwordKey, String JavaDoc debugFlag)
266         throws SQLException JavaDoc
267     {
268         if (verbose)
269             logMessage("Testing : " + debugFlag);
270         
271         // set the debug flag to crash.
272
setDebugFlag(debugFlag);
273         SQLException JavaDoc sqle = null;
274         try {
275             Connection JavaDoc conn = bootDatabase(passwordKey);
276         } catch (SQLException JavaDoc se) {
277             // recovery of the database
278
// shold have failed at the specified
279
// debug flag.
280
sqle = se;
281         }
282         // check that database boot failed at the set debug flag.
283
verifyException(sqle, debugFlag);
284         // clear the debug flag.
285
clearDebugFlag(debugFlag);
286     }
287     
288
289
290     /*
291      * Recover the database that failied during re-encryption and
292      * perform some simple sanity check on the database.
293      */

294     private void recover(int passwordKey)
295         throws SQLException JavaDoc
296     {
297         // starting recovery of database with failed Re-encrytpion
298
// in debug mode;
299

300         Connection JavaDoc conn = bootDatabase(passwordKey);
301
302         // verify the contents of the db are ok.
303
runConsistencyChecker(conn, TEST_TABLE_NAME);
304         // insert some rows, this might fail if anyhing is
305
// wrong in the logging system setup.
306
insert(conn, TEST_TABLE_NAME, 100);
307         conn.commit();
308         conn.close();
309     }
310     
311
312
313     /** *************************************************
314      * Crash/recovery test scenarios during
315      * encryption of an un-encrypted database.
316      ****************************************************/

317     
318
319
320     // Debug flags that are to be set to simulate a crash
321
// at different points during (re)encryption of the database.
322
// these flags should match the flags in the engine code;
323
// these are redifined here to avoid pulling the engine code
324
// into the tests.
325

326     
327     /*
328       Set to true if we want the re-encryption to crash just
329       before the commit.
330     */

331
332     public static final String JavaDoc TEST_REENCRYPT_CRASH_BEFORE_COMMT =
333         SanityManager.DEBUG ? "TEST_REENCRYPT_CRASH_BEFORE_COMMT" : null ;
334     public static final String JavaDoc TEST_REENCRYPT_CRASH_AFTER_COMMT =
335         SanityManager.DEBUG ? "TEST_REENCRYPT_CRASH_AFTER_COMMT" : null ;
336     public static final String JavaDoc TEST_REENCRYPT_CRASH_AFTER_SWITCH_TO_NEWKEY =
337         SanityManager.DEBUG ? "TEST_REENCRYPT_CRASH_AFTER_SWITCH_TO_NEWKEY" : null ;
338     public static final String JavaDoc TEST_REENCRYPT_CRASH_AFTER_CHECKPOINT =
339         SanityManager.DEBUG ? "TEST_REENCRYPT_CRASH_AFTER_CHECKPOINT" : null ;
340                                             
341     public static final String JavaDoc
342         TEST_REENCRYPT_CRASH_AFTER_RECOVERY_UNDO_LOGFILE_DELETE =
343         SanityManager.DEBUG ?
344         "TEST_REENCRYPT_CRASH_AFTER_RECOVERY_UNDO_LOGFILE_DELETE" : null;
345     public static final String JavaDoc
346         TEST_REENCRYPT_CRASH_AFTER_RECOVERY_UNDO_REVERTING_KEY =
347         SanityManager.DEBUG ?
348         "TEST_REENCRYPT_CRASH_AFTER_RECOVERY_UNDO_REVERTING_KEY" : null;
349     public static final String JavaDoc
350         TEST_REENCRYPT_CRASH_BEFORE_RECOVERY_FINAL_CLEANUP =
351         SanityManager.DEBUG ?
352         "TEST_REENCRYPT_CRASH_BEFORE_RECOVERY_FINAL_CLEANUP" : null;
353     
354     
355     void setDebugFlag(String JavaDoc debugFlag) {
356         if (SanityManager.DEBUG) {
357             SanityManager.DEBUG_SET(debugFlag);
358         }
359     }
360
361     void clearDebugFlag(String JavaDoc debugFlag) {
362         if (SanityManager.DEBUG) {
363             SanityManager.DEBUG_CLEAR(debugFlag);
364         }
365     }
366
367     /*
368      * verify that database boot failed when a debug flag is set.
369      */

370     private void verifyException(SQLException JavaDoc sqle, String JavaDoc debugFlag)
371     {
372         boolean expectedExcepion = false ;
373         if (sqle != null)
374         {
375             
376             if (sqle.getSQLState() != null &&
377                 sqle.getSQLState().equals("XJ040"))
378             {
379                 // boot failed as expected with the debug flag
380
// now check if it failed with specifed debug flags.
381
SQLException JavaDoc ne = sqle.getNextException();
382                 if (ne != null) {
383                     String JavaDoc message = ne.getMessage();
384                     // check if debug flag exists in the message
385
if (message.indexOf(debugFlag) != -1)
386                     {
387                         expectedExcepion = true;
388                     }
389                 }
390             }
391
392             if (!expectedExcepion)
393                 dumpSQLException(sqle);
394         }
395         else
396         {
397             if (SanityManager.DEBUG)
398             {
399                 logMessage("Did not crash at " + debugFlag);
400             }
401         }
402     }
403
404
405     /*
406      * create the tables that are used by this test.
407      * @param conn connection to the database.
408      * @param tableName Name of the table to create.
409      * @exception SQLException if any database exception occurs.
410      */

411     void createTable(Connection JavaDoc conn,
412                      String JavaDoc tableName) throws SQLException JavaDoc {
413
414             Statement JavaDoc s = conn.createStatement();
415             s.executeUpdate("CREATE TABLE " + tableName +
416                             "(id INT," +
417                             "name CHAR(200))");
418             s.executeUpdate("create index " + tableName + "_id_idx on " +
419                             tableName + "(id)");
420             s.close();
421     }
422
423
424     /**
425      * Run some consistency checks.
426      * @param conn connection to the database.
427      * @param tableName consistency checks are performed on this table.
428      * @exception SQLException if any database exception occurs.
429      */

430     void runConsistencyChecker(Connection JavaDoc conn,
431                                String JavaDoc tableName) throws SQLException JavaDoc {
432         Statement JavaDoc stmt = conn.createStatement();
433         stmt.execute("values SYSCS_UTIL.SYSCS_CHECK_TABLE('APP', 'EMP')");
434         // check the data in the EMP table.
435
select(conn, tableName);
436     }
437
438             
439     /**
440      * Insert some rows into the specified table.
441      * @param conn connection to the database.
442      * @param tableName name of the table that rows are inserted.
443      * @param rowCount Number of rows to Insert.
444      * @exception SQLException if any database exception occurs.
445      */

446     void insert(Connection JavaDoc conn,
447                 String JavaDoc tableName,
448                 int rowCount) throws SQLException JavaDoc
449     {
450
451         PreparedStatement JavaDoc ps = conn.prepareStatement("INSERT INTO " +
452                                                      tableName +
453                                                      " VALUES(?,?)");
454         int startId = findMax(conn, tableName);
455         for (int i = startId; i < rowCount; i++) {
456             
457             ps.setInt(1, i); // ID
458
ps.setString(2 , "skywalker" + i);
459             ps.executeUpdate();
460         }
461         ps.close();
462         conn.commit();
463     }
464
465     /**
466     * find a max value on the give table.
467     * @param conn connection to the database.
468     * @param tableName name of the table.
469     * @exception SQLException if any database exception occurs.
470     */

471     private int findMax(Connection JavaDoc conn,
472                         String JavaDoc tableName) throws SQLException JavaDoc
473     {
474         Statement JavaDoc s = conn.createStatement();
475         ResultSet JavaDoc rs = s.executeQuery("SELECT max(ID) from " +
476                                           tableName);
477         rs.next();
478         int max = rs.getInt(1);
479         rs.close();
480         s.close();
481         return max;
482     }
483
484
485     /*
486      * read the rows in the table.
487      * @param conn connection to the database.
488      * @param tableName select operation is perfomed on this table.
489      * @exception SQLException if any database exception occurs.
490      */

491     void select(Connection JavaDoc conn ,
492                 String JavaDoc tableName) throws SQLException JavaDoc
493     {
494         
495         Statement JavaDoc s = conn.createStatement();
496         ResultSet JavaDoc rs = s.executeQuery("SELECT ID, name from " +
497                                           tableName + " order by id" );
498         int count = 0;
499         int id = 0;
500         while(rs.next())
501         {
502             int tid = rs.getInt(1);
503             String JavaDoc name = rs.getString(2);
504             if(name.equals("skywalker" + id) && tid!= id)
505             {
506                 logMessage("DATA IN THE TABLE IS NOT AS EXPECTED");
507                 logMessage("Got :ID=" + tid + " Name=:" + name);
508                 logMessage("Expected: ID=" + id + "Name=" + "skywalker" + id );
509             }
510
511             id++;
512             count++;
513         }
514             
515         rs.close();
516         s.close();
517         conn.commit();
518     }
519
520
521
522     /*
523      * create an encrypted database.
524      */

525     private Connection JavaDoc createEncryptedDatabase() throws SQLException JavaDoc
526     {
527         String JavaDoc connAttrs = "";
528         if (encryptionType == USING_PASSWORD)
529         {
530             // create encrypted database.
531
connAttrs = "create=true;dataEncryption=true;bootPassword=" +
532                 OLD_PASSWORD;
533         }
534
535         if (encryptionType == USING_KEY)
536         {
537             // create an encrypted database.
538
connAttrs = "create=true;dataEncryption=true;encryptionKey=" +
539                 OLD_KEY;
540         }
541         
542         return TestUtil.getConnection(currentTestDatabase, connAttrs);
543     }
544
545
546     /*
547      * create an un-encrypted database.
548      */

549     private Connection JavaDoc createDatabase() throws SQLException JavaDoc
550     {
551         return TestUtil.getConnection(currentTestDatabase,
552                                       "create=true" );
553     }
554
555
556     /**
557      * Re-encrypt the database.
558      * @exception SQLException if any database exception occurs.
559      */

560     private Connection JavaDoc reEncryptDatabase() throws SQLException JavaDoc
561     {
562         String JavaDoc connAttrs = "";
563         if (encryptionType == USING_PASSWORD)
564         {
565             // re-encrypt the database.
566
connAttrs = "bootPassword=" + OLD_PASSWORD +
567                 ";newBootPassword=" + NEW_PASSWORD;
568         }
569
570         if (encryptionType == USING_KEY)
571         {
572             // re-encrypt the database.
573
connAttrs = "encryptionKey=" + OLD_KEY +
574                 ";newEncryptionKey=" + NEW_KEY;
575         }
576         
577         if (verbose)
578             logMessage("re-encrypting " + currentTestDatabase +
579                        " with " + connAttrs);
580
581         return TestUtil.getConnection(currentTestDatabase, connAttrs);
582     }
583
584     
585     /**
586      * Encrypt an un-encrypted atabase.
587      * @param password boot password of the database.
588      * @exception SQLException if any database exception occurs.
589      */

590     private Connection JavaDoc encryptDatabase()
591         throws SQLException JavaDoc
592     {
593         String JavaDoc connAttrs = "";
594         if (encryptionType == USING_PASSWORD)
595         {
596             //encrypt an existing database.
597
connAttrs = "dataEncryption=true;bootPassword=" + OLD_PASSWORD;
598         }
599         if (encryptionType == USING_KEY)
600         {
601             //encrypt an existing database.
602
connAttrs = "dataEncryption=true;encryptionKey=" + OLD_KEY;
603         }
604
605         if (verbose)
606             logMessage("encrypting " + currentTestDatabase +
607                        " with " + connAttrs);
608         return TestUtil.getConnection(currentTestDatabase, connAttrs);
609     }
610     
611
612     /**
613      * Boot the database.
614      * @param passwordOrKey the password/key to use.
615      * @exception SQLException if any database exception occurs.
616      */

617     Connection JavaDoc bootDatabase(int passwordKey)
618         throws SQLException JavaDoc
619     {
620
621         String JavaDoc connAttrs = "";
622         if (encryptionType == USING_PASSWORD)
623         {
624             if (passwordKey == NEW)
625                 connAttrs = "bootPassword=" + NEW_PASSWORD;
626             else if (passwordKey == OLD)
627                 connAttrs = "bootPassword=" + OLD_PASSWORD;
628         }
629
630         
631         if (encryptionType == USING_KEY)
632         {
633             if (passwordKey == NEW)
634                 connAttrs = "encryptionKey=" + NEW_KEY;
635             else if (passwordKey == OLD)
636                 connAttrs = "encryptionKey=" + OLD_KEY;
637         }
638
639         if (verbose)
640             logMessage("booting " + currentTestDatabase +
641                    " with " + connAttrs);
642         return TestUtil.getConnection(currentTestDatabase, connAttrs);
643     }
644
645
646
647     /**
648      * Shutdown the datbase
649      */

650     void shutdown() {
651
652         if (verbose)
653             logMessage("Shutdown " + currentTestDatabase);
654         try{
655             //shutdown
656
TestUtil.getConnection(currentTestDatabase, "shutdown=true");
657         }catch(SQLException JavaDoc se){
658             if (se.getSQLState() == null || !(se.getSQLState().equals("08006")))
659             {
660                 // database was not shutdown properly
661
dumpSQLException(se);
662             }
663         }
664         
665
666     }
667
668     /**
669      * dump the SQLException to the standard output.
670      */

671     private void dumpSQLException(SQLException JavaDoc sqle) {
672         
673         org.apache.derby.tools.JDBCDisplayUtil. ShowSQLException(System.out, sqle);
674         sqle.printStackTrace(System.out);
675     }
676
677
678     void logMessage(String JavaDoc str)
679     {
680         System.out.println(str);
681     }
682     
683     
684     public static void main(String JavaDoc[] argv) throws Throwable JavaDoc {
685         
686         ReEncryptCrashRecovery test = new ReEncryptCrashRecovery();
687         try {
688             test.runTest();
689         }
690         catch (SQLException JavaDoc sqle) {
691             org.apache.derby.tools.JDBCDisplayUtil.ShowSQLException(
692                 System.out, sqle);
693             sqle.printStackTrace(System.out);
694         }
695     }
696 }
697
Popular Tags