1 /*- 2 * See the file LICENSE for redistribution information. 3 * 4 * Copyright (c) 2002, 2014 Oracle and/or its affiliates. All rights reserved. 5 * 6 * $Id$ 7 */ 8 package com.sleepycat.je.trigger; 9 10 import static org.junit.Assert.assertEquals; 11 import static org.junit.Assert.assertNull; 12 13 import java.util.Arrays; 14 import java.util.LinkedList; 15 import java.util.List; 16 17 import org.junit.After; 18 import org.junit.Before; 19 import org.junit.Test; 20 21 import com.sleepycat.je.Database; 22 import com.sleepycat.je.DatabaseConfig; 23 import com.sleepycat.je.DatabaseEntry; 24 import com.sleepycat.je.DbInternal; 25 import com.sleepycat.je.Environment; 26 import com.sleepycat.je.OperationStatus; 27 import com.sleepycat.je.Transaction; 28 import com.sleepycat.je.TransactionConfig; 29 import com.sleepycat.je.dbi.DatabaseImpl; 30 import com.sleepycat.je.dbi.TriggerManager.MapOver; 31 32 /** 33 * This set of unit tests exercises all standalone trigger invocations. 34 */ 35 36 public class InvokeTest extends TestBase { 37 38 Environment env; 39 Database db1 = null; 40 int triggerCount = -1; 41 /* The number of nodes. it's > 1 if this is a replicated environment. */ 42 protected int nNodes = 1; 43 getTriggers()44 protected List<Trigger> getTriggers() { 45 return new LinkedList<Trigger>(Arrays.asList((Trigger) new DBT("t1"), 46 (Trigger) new DBT("t2"))); 47 } 48 getTransientTriggers()49 protected List<Trigger> getTransientTriggers() { 50 return new LinkedList<Trigger>(Arrays.asList((Trigger) new TDBT("tt1"), 51 (Trigger) new TDBT("tt2"))); 52 } 53 getTriggersPlusOne()54 protected List<Trigger> getTriggersPlusOne() { 55 List<Trigger> triggers = getTriggers(); 56 triggers.add(new InvokeTest.DBT("t3")); 57 return triggers; 58 } 59 getTransactionConfig()60 protected TransactionConfig getTransactionConfig() { 61 return null; 62 } 63 64 @Before setUp()65 public void setUp() 66 throws Exception { 67 68 super.setUp(); 69 List<Trigger> triggers = getTriggers(); 70 triggerCount = triggers.size(); 71 dbConfig.setTriggers(triggers); 72 73 dbConfig.setOverrideTriggers(true); 74 75 env = create(envRoot, envConfig); 76 Transaction transaction = 77 env.beginTransaction(null, getTransactionConfig()); 78 db1 = env.openDatabase(transaction, "db1", dbConfig); 79 transaction.commit(); 80 dbConfig.setOverrideTriggers(false); 81 resetTriggers(); 82 } 83 84 @After tearDown()85 public void tearDown() 86 throws Exception { 87 88 db1.close(); 89 close(env); 90 super.tearDown(); 91 } 92 93 @Test testAddRemoveTriggerExistindDbTrans()94 public void testAddRemoveTriggerExistindDbTrans() { 95 Transaction transaction = 96 env.beginTransaction(null, getTransactionConfig()); 97 addRemoveTriggerExistingDb(transaction); 98 transaction.commit(); 99 } 100 101 @Test testAddRemoveTriggerExistindDbAuto()102 public void testAddRemoveTriggerExistindDbAuto() { 103 addRemoveTriggerExistingDb(null); 104 } 105 addRemoveTriggerExistingDb(Transaction transaction)106 void addRemoveTriggerExistingDb(Transaction transaction) { 107 db1.close(); 108 resetTriggers(); 109 110 /* read/write open. */ 111 db1 = env.openDatabase(transaction, "db1", dbConfig); 112 verifyAddTrigger(0); 113 verifyRemoveTrigger(0); 114 checkNullOpenTriggerCount(1); 115 db1.close(); 116 resetTriggers(); 117 118 dbConfig.setOverrideTriggers(true); 119 dbConfig.setTriggers(getTriggersPlusOne()); 120 db1 = env.openDatabase(transaction, "db1", dbConfig); 121 DatabaseImpl db1Impl = DbInternal.getDatabaseImpl(db1); 122 DBT t3 = (DBT) db1Impl.getTriggers().get(2); 123 assertEquals("t3", t3.getName()); 124 assertEquals(1, t3.ts.nAddTrigger); 125 assertEquals(0, t3.ts.nRemoveTrigger); 126 db1.close(); 127 resetTriggers(); 128 129 dbConfig.setTriggers(getTriggers()); 130 db1 = env.openDatabase(transaction, "db1", dbConfig); 131 db1Impl = DbInternal.getDatabaseImpl(db1); 132 assertEquals("t3", t3.getName()); 133 assertEquals(0, t3.ts.nAddTrigger); 134 assertEquals(1, t3.ts.nRemoveTrigger); 135 } 136 137 /** 138 * Simply verifies that transient triggers are indeed transient, i.e., not 139 * stored in the DatabaseImpl. Also checks that a transient trigger can be 140 * added when setOverrideTriggers(true) is not called. 141 */ 142 @Test testBasicTransientTrigger()143 public void testBasicTransientTrigger() { 144 145 /* Set two transient triggers in new DB. */ 146 List<Trigger> tList = getTransientTriggers(); 147 DatabaseConfig tdbConfig = getDBConfig(); 148 tdbConfig.setOverrideTriggers(false); 149 tdbConfig.setTriggers(tList); 150 Database dbc = env.openDatabase(null, "dbc", tdbConfig); 151 assertEquals(tList, dbc.getConfig().getTriggers()); 152 for (Trigger trigger : dbc.getConfig().getTriggers()) { 153 assertEquals("dbc", trigger.getDatabaseName()); 154 } 155 verifyAddTrigger(1); 156 checkTransientTriggerCount(1); 157 resetTriggers(); 158 159 /* Test simple transctional put(). */ 160 Transaction transaction = env.beginTransaction(null, null); 161 DatabaseEntry key = new DatabaseEntry(new byte[] {1}); 162 DatabaseEntry data = new DatabaseEntry(new byte[] {2}); 163 dbc.put(transaction, key, data); 164 verifyPut(1, key, data, null); 165 transaction.commit(); 166 verifyCommit(1); 167 checkTransientTriggerCount(1); 168 resetTriggers(); 169 170 /* Close DB and reopen -- there should be no triggers. */ 171 dbc.close(); 172 verifyRemoveTrigger(1); 173 checkTransientTriggerCount(1); 174 resetTriggers(); 175 tdbConfig = getDBConfig(); 176 tdbConfig.setOverrideTriggers(false); 177 tdbConfig.setAllowCreate(false); 178 dbc = env.openDatabase(null, "dbc", tdbConfig); 179 assertNull(dbc.getConfig().getTriggers()); 180 verifyAddTrigger(0); 181 dbc.close(); 182 verifyRemoveTrigger(0); 183 checkTransientTriggerCount(0); 184 185 /* Remove DB and recreate -- there should be no triggers. */ 186 env.removeDatabase(null, "dbc"); 187 tdbConfig = getDBConfig(); 188 tdbConfig.setOverrideTriggers(false); 189 dbc = env.openDatabase(null, "dbc", tdbConfig); 190 assertNull(dbc.getConfig().getTriggers()); 191 verifyAddTrigger(0); 192 dbc.close(); 193 verifyRemoveTrigger(0); 194 checkTransientTriggerCount(0); 195 196 /* Add triggers to existing DB without overriding config. */ 197 tdbConfig = getDBConfig(); 198 tdbConfig.setOverrideTriggers(false); 199 tdbConfig.setTriggers(tList); 200 tdbConfig.setAllowCreate(false); 201 dbc = env.openDatabase(null, "dbc", tdbConfig); 202 assertEquals(tList, dbc.getConfig().getTriggers()); 203 for (Trigger trigger : dbc.getConfig().getTriggers()) { 204 assertEquals("dbc", trigger.getDatabaseName()); 205 } 206 verifyAddTrigger(1); 207 dbc.close(); 208 verifyRemoveTrigger(1); 209 checkTransientTriggerCount(1); 210 resetTriggers(); 211 212 /* Clean up. */ 213 env.removeDatabase(null, "dbc"); 214 } 215 create(Transaction transaction)216 private void create(Transaction transaction) { 217 List<Trigger> tgs = getTriggers(); 218 DatabaseConfig tdbConfig = getDBConfig(); 219 dbConfig.setOverrideTriggers(true); 220 tdbConfig.setTriggers(tgs); 221 verifyOpen(0, 0); 222 Database dbc = env.openDatabase(transaction, "dbc", tdbConfig ); 223 224 for (Trigger trigger : dbc.getConfig().getTriggers()) { 225 assertEquals("dbc", trigger.getDatabaseName()); 226 } 227 verifyOpen(1, 1); 228 verifyAddTrigger(1); 229 230 if (transaction == null) { 231 verifyCommit(1); 232 } 233 dbc.close(); 234 verifyClose(1); 235 /* Read triggers from the existing database. */ 236 checkTriggerCount(1); 237 resetTriggers(); 238 dbc = env.openDatabase(transaction, "dbc", dbConfig); 239 verifyOpen(0,1); 240 verifyAddTrigger(0); 241 /* Not a new database no create and consequently commit triggers. */ 242 verifyCommit(0); 243 checkNullOpenTriggerCount(1); 244 assertEquals(2, db1.getConfig().getTriggers().size()); 245 dbc.close(); 246 env.removeDatabase(transaction, "dbc"); 247 } 248 249 @Test testCreateAuto()250 public void testCreateAuto() { 251 create(null); 252 } 253 254 @Test testCreateTrans()255 public void testCreateTrans() { 256 Transaction transaction = 257 env.beginTransaction(null, getTransactionConfig()); 258 create(transaction); 259 transaction.commit(); 260 } 261 262 @Test testOpenAuto()263 public void testOpenAuto() { 264 open(null); 265 } 266 267 @Test testOpenTrans()268 public void testOpenTrans() { 269 Transaction transaction = 270 env.beginTransaction(null, getTransactionConfig()); 271 open(transaction); 272 transaction.commit(); 273 } 274 open(Transaction transaction)275 private void open(Transaction transaction) { 276 db1.close(); 277 resetTriggers(); 278 /* read/write open. */ 279 db1 = env.openDatabase(transaction, "db1", dbConfig); 280 verifyOpen(0,1); 281 checkNullOpenTriggerCount(1); 282 db1.close(); 283 resetTriggers(); 284 285 DatabaseConfig config = getDBConfig(); 286 config.setReadOnly(true); 287 db1 = env.openDatabase(transaction, "db1", config); 288 verifyOpen(0,0); 289 checkTriggerCount(0); 290 resetTriggers(); 291 292 config.setReadOnly(false); 293 Database db11 = env.openDatabase(transaction, "db1", config); 294 verifyOpen(0,1); 295 checkNullOpenTriggerCount(1); 296 db11.close(); 297 resetTriggers(); 298 } 299 300 @Test testClose()301 public void testClose() { 302 closeDb(); 303 } 304 closeDb()305 private void closeDb() { 306 resetTriggers(); 307 db1.close(); 308 verifyClose(1); 309 checkNullOpenTriggerCount(1); 310 } 311 rename(Transaction transaction)312 private void rename(Transaction transaction) { 313 db1.close(); 314 env.renameDatabase(transaction, "db1", "dbr1"); 315 verifyRename("dbr1", 1); 316 checkTriggerCount(1); 317 } 318 319 @Test testRenameAuto()320 public void testRenameAuto() { 321 rename(null); 322 } 323 324 @Test testRenameTrans()325 public void testRenameTrans() { 326 Transaction transaction = env.beginTransaction(null, null); 327 rename(transaction); 328 checkTriggerCount(1); 329 transaction.commit(); 330 } 331 332 @Test testRenameAbort()333 public void testRenameAbort() { 334 Transaction transaction = env.beginTransaction(null, null); 335 rename(transaction); 336 checkTriggerCount(1); 337 transaction.abort(); 338 db1 = env.openDatabase(null, "db1", dbConfig); 339 verifyDB1Triggers(); 340 verifyAbort(1); 341 } 342 truncate(Transaction transaction)343 private void truncate(Transaction transaction) { 344 db1.close(); 345 env.truncateDatabase(transaction, "db1", false); 346 verifyTruncate(1); 347 checkTriggerCount(1); 348 } 349 350 @Test testTruncateAuto()351 public void testTruncateAuto() { 352 truncate(null); 353 } 354 355 @Test testTruncateTrans()356 public void testTruncateTrans() { 357 Transaction transaction = env.beginTransaction(null, null); 358 truncate(transaction); 359 transaction.commit(); 360 361 /* 362 * Truncate does a rename under the covers so make sure the triggers 363 * are present on the new empty database. 364 */ 365 db1 = env.openDatabase(null, "db1", dbConfig); 366 verifyDB1Triggers(); 367 } 368 verifyDB1Triggers()369 private void verifyDB1Triggers() { 370 assertEquals("db1", db1.getDatabaseName()); 371 assertEquals(triggerCount, db1.getConfig().getTriggers().size()); 372 for (Trigger t : db1.getConfig().getTriggers()) { 373 assertEquals("db1", t.getDatabaseName()); 374 } 375 } 376 remove(Transaction transaction)377 private void remove(Transaction transaction) { 378 db1.close(); 379 env.removeDatabase(transaction, "db1"); 380 verifyRemove(1); 381 verifyRemoveTrigger(1); 382 checkTriggerCount(1); 383 } 384 385 @Test testRemoveAuto()386 public void testRemoveAuto() { 387 remove(null); 388 } 389 390 @Test testRemoveTrans()391 public void testRemoveTrans() { 392 Transaction transaction = env.beginTransaction(null, null); 393 remove(transaction); 394 transaction.commit(); 395 } 396 397 @Test testKVOpsAuto()398 public void testKVOpsAuto() { 399 KVOps(null); 400 } 401 402 @Test testKVOpsTrans()403 public void testKVOpsTrans() { 404 Transaction transaction = env.beginTransaction(null, null); 405 KVOps(transaction); 406 transaction.commit(); 407 } 408 409 @Test testKVOpsAbort()410 public void testKVOpsAbort() { 411 Transaction transaction = env.beginTransaction(null, null); 412 KVOps(transaction); 413 transaction.abort(); 414 verifyAbort(1); 415 } 416 KVOps(Transaction transaction)417 private void KVOps(Transaction transaction) { 418 419 DatabaseEntry key = new DatabaseEntry(); 420 key.setData(new byte[]{1}); 421 DatabaseEntry data1 = new DatabaseEntry(); 422 data1.setData(new byte[]{2}); 423 verifyPut(0, null, null, null); 424 425 db1.put(transaction, key, data1); 426 verifyPut(1, key, data1, null); 427 checkTriggerCount(1); 428 resetTriggers(); 429 DatabaseEntry data2 = new DatabaseEntry(); 430 data2.setData(new byte[]{3}); 431 db1.put(transaction, key, data2); 432 verifyPut(1, key, data2, data1); 433 checkTriggerCount(1); 434 resetTriggers(); 435 436 OperationStatus status = db1.delete(transaction, key); 437 assertEquals(OperationStatus.SUCCESS, status); 438 verifyDelete(1, key, data2); 439 checkTriggerCount(1); 440 resetTriggers(); 441 442 status = db1.delete(transaction, key); 443 assertEquals(OperationStatus.NOTFOUND, status); 444 verifyDelete(0, null, null); 445 checkTriggerCount(0); 446 447 db1.close(); 448 } 449 450 /** 451 * Ensure recovery replay of MapLNs executes properly. This tests a fix 452 * for an NPE that occurred during DatabaseImpl.readFromLog, which was 453 * calling DatabaseImpl.getName prior to instantiation of DbTree. 454 */ 455 @Test testBasicRecovery()456 public void testBasicRecovery() { 457 db1.close(); 458 close(env); 459 resetTriggers(); 460 env = create(envRoot, envConfig); 461 db1 = env.openDatabase(null, "db1", dbConfig); 462 checkNullOpenTriggerCount(1); 463 verifyAddTrigger(0); 464 verifyRemoveTrigger(0); 465 } 466 resetTriggers()467 private void resetTriggers() { 468 for (Trigger t : TestBase.invokedTriggers) { 469 ((TDBT)t).clear(); 470 } 471 TestBase.invokedTriggers.clear(); 472 } 473 verifyDelete(final int nDelete, final DatabaseEntry key, final DatabaseEntry oldData)474 protected void verifyDelete(final int nDelete, 475 final DatabaseEntry key, 476 final DatabaseEntry oldData) { 477 478 new MapOver<Trigger,Trigger>(TestBase.invokedTriggers) { 479 480 @Override 481 protected Trigger fun(Trigger e) { 482 final TDBT dbt = (TDBT)e; 483 assertEquals(nDelete, dbt.ts.nDelete); 484 assertEquals(key, dbt.ts.key); 485 assertEquals(oldData, dbt.ts.oldData); 486 dbt.ts.nDelete = 0; 487 return e; 488 } 489 }.run(); 490 } 491 verifyPut(final int nPut, final DatabaseEntry key, final DatabaseEntry newData, final DatabaseEntry oldData)492 protected void verifyPut(final int nPut, 493 final DatabaseEntry key, 494 final DatabaseEntry newData, 495 final DatabaseEntry oldData) {; 496 497 new MapOver<Trigger,Trigger>(TestBase.invokedTriggers) { 498 499 @Override 500 protected Trigger fun(Trigger e) { 501 final TDBT dbt = (TDBT)e; 502 assertEquals(nPut, dbt.ts.nPut); 503 dbt.ts.nPut = 0; 504 assertEquals(key, dbt.ts.key); 505 assertEquals(newData, dbt.ts.newData); 506 assertEquals(oldData, dbt.ts.oldData); 507 return e; 508 } 509 }.run(); 510 } 511 verifyOpen(final int nCreate, final int nOpen)512 protected void verifyOpen(final int nCreate, final int nOpen) { 513 514 new MapOver<Trigger,Trigger>(TestBase.invokedTriggers) { 515 516 @Override 517 protected Trigger fun(Trigger e) { 518 assertEquals(nOpen, ((TDBT)e).ts.nOpen); 519 assertEquals(nCreate, ((TDBT)e).ts.nCreate); 520 ((TDBT)e).ts.nCreate = 0; 521 ((TDBT)e).ts.nOpen = 0; 522 return e; 523 } 524 }.run(); 525 } 526 verifyClose(final int nClose)527 protected void verifyClose(final int nClose) { 528 529 new MapOver<Trigger,Trigger>(TestBase.invokedTriggers) { 530 531 @Override 532 protected Trigger fun(Trigger e) { 533 assertEquals(nClose, ((TDBT)e).ts.nClose); 534 ((TDBT)e).ts.nClose = 0; 535 return e; 536 } 537 }.run(); 538 } 539 540 /* The triggers should have been executed on all nodes. */ checkTriggerCount(final int count)541 protected void checkTriggerCount(final int count) { 542 assertEquals((count * nNodes * triggerCount), 543 TestBase.invokedTriggers.size()); 544 } 545 546 /* Transient triggers are only executed on the node they're configured. */ checkTransientTriggerCount(final int count)547 protected void checkTransientTriggerCount(final int count) { 548 assertEquals((count * triggerCount), 549 TestBase.invokedTriggers.size()); 550 } 551 552 /* 553 * Null open triggers, ones where the db is opened for writes, 554 * but no writes are actually done, will not fire on replica nodes. 555 */ checkNullOpenTriggerCount(final int count)556 protected void checkNullOpenTriggerCount(final int count) { 557 assertEquals((count * triggerCount), 558 TestBase.invokedTriggers.size()); 559 } 560 verifyRemove(final int nRemove)561 protected void verifyRemove(final int nRemove) { 562 563 new MapOver<Trigger,Trigger>(TestBase.invokedTriggers) { 564 565 @Override 566 protected Trigger fun(Trigger e) { 567 assertEquals(nRemove, ((DBT)e).ts.nRemove); 568 ((DBT)e).ts.nRemove = 0; 569 return e; 570 } 571 }.run(); 572 } 573 verifyTruncate(final int nTruncate)574 protected void verifyTruncate(final int nTruncate) { 575 576 new MapOver<Trigger,Trigger>(TestBase.invokedTriggers) { 577 578 @Override 579 protected Trigger fun(Trigger e) { 580 assertEquals(nTruncate, ((DBT)e).ts.nTruncate); 581 ((DBT)e).ts.nTruncate = 0; 582 return e; 583 } 584 }.run(); 585 } 586 verifyRename(final String newName, final int nRename)587 protected void verifyRename(final String newName, 588 final int nRename) { 589 590 new MapOver<Trigger,Trigger>(TestBase.invokedTriggers) { 591 592 @Override 593 protected Trigger fun(Trigger e) { 594 final DBT dbt = (DBT)e; 595 assertEquals(nRename, dbt.ts.nRename); 596 newName.equals(dbt.ts.newName); 597 dbt.ts.nRename = 0; 598 assertEquals(newName, dbt.getDatabaseName()); 599 return e; 600 } 601 }.run(); 602 } 603 verifyCommit(final int nCommit)604 protected void verifyCommit(final int nCommit) { 605 606 new MapOver<Trigger,Trigger>(TestBase.invokedTriggers) { 607 608 @Override 609 protected Trigger fun(Trigger e) { 610 assertEquals(nCommit, ((TDBT)e).ts.nCommit); 611 ((TDBT)e).ts.nCommit = 0; 612 return e; 613 } 614 }.run(); 615 } 616 verifyAbort(final int nAbort)617 protected void verifyAbort(final int nAbort) { 618 619 new MapOver<Trigger,Trigger>(TestBase.invokedTriggers) { 620 621 @Override 622 protected Trigger fun(Trigger e) { 623 assertEquals(nAbort, ((TDBT)e).ts.nAbort); 624 ((TDBT)e).ts.nAbort = 0; 625 return e; 626 } 627 }.run(); 628 } 629 verifyRemoveTrigger(final int nRemoveTrigger)630 protected void verifyRemoveTrigger(final int nRemoveTrigger) { 631 632 new MapOver<Trigger,Trigger>(TestBase.invokedTriggers) { 633 634 @Override 635 protected Trigger fun(Trigger e) { 636 assertEquals(nRemoveTrigger, ((TDBT)e).ts.nRemoveTrigger); 637 ((TDBT)e).ts.nRemoveTrigger = 0; 638 return e; 639 } 640 }.run(); 641 } 642 verifyAddTrigger(final int nAddTrigger)643 protected void verifyAddTrigger(final int nAddTrigger) { 644 645 new MapOver<Trigger,Trigger>(TestBase.invokedTriggers) { 646 647 @Override 648 protected Trigger fun(Trigger e) { 649 assertEquals(nAddTrigger, ((TDBT)e).ts.nAddTrigger); 650 ((TDBT)e).ts.nAddTrigger = 0; 651 return e; 652 } 653 }.run(); 654 } 655 } 656