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 */ 7 8 package com.sleepycat.je.util; 9 10 import java.io.BufferedReader; 11 import java.io.Closeable; 12 import java.io.File; 13 import java.io.FileInputStream; 14 import java.io.FileReader; 15 import java.io.FileOutputStream; 16 import java.io.FileWriter; 17 import java.io.FilenameFilter; 18 import java.io.IOException; 19 import java.io.InputStream; 20 import java.io.ObjectInputStream; 21 import java.io.ObjectOutputStream; 22 import java.io.OutputStream; 23 import java.text.NumberFormat; 24 import java.util.ArrayList; 25 import java.util.Random; 26 27 import junit.framework.TestCase; 28 29 import com.sleepycat.je.CacheMode; 30 import com.sleepycat.je.Cursor; 31 import com.sleepycat.je.Database; 32 import com.sleepycat.je.DatabaseException; 33 import com.sleepycat.je.DbInternal; 34 import com.sleepycat.je.DbTestProxy; 35 import com.sleepycat.je.Environment; 36 import com.sleepycat.je.EnvironmentConfig; 37 import com.sleepycat.je.ExceptionEvent; 38 import com.sleepycat.je.ExceptionListener; 39 import com.sleepycat.je.StatsConfig; 40 import com.sleepycat.je.dbi.CursorImpl; 41 import com.sleepycat.je.dbi.EnvironmentImpl; 42 import com.sleepycat.je.latch.LatchSupport; 43 import com.sleepycat.je.log.FileManager; 44 import com.sleepycat.je.tree.BIN; 45 import com.sleepycat.je.tree.ChildReference; 46 import com.sleepycat.je.tree.IN; 47 import com.sleepycat.je.tree.LN; 48 import com.sleepycat.je.tree.SearchResult; 49 import com.sleepycat.je.tree.Tree; 50 import com.sleepycat.je.tree.WithRootLatched; 51 import com.sleepycat.util.test.SharedTestUtils; 52 import com.sleepycat.utilint.StringUtils; 53 54 public class TestUtils { 55 public static String DEST_DIR = SharedTestUtils.DEST_DIR; 56 public static String NO_SYNC = SharedTestUtils.NO_SYNC; 57 58 public static final String LOG_FILE_NAME = "00000000.jdb"; 59 60 public static final StatsConfig FAST_STATS; 61 62 static { 63 FAST_STATS = new StatsConfig(); 64 FAST_STATS.setFast(true); 65 } 66 67 private static final boolean DEBUG = true; 68 private static Random rnd = new Random(); 69 debugMsg(String message)70 public void debugMsg(String message) { 71 72 if (DEBUG) { 73 System.out.println 74 (Thread.currentThread().toString() + " " + message); 75 } 76 } 77 setRandomSeed(int seed)78 static public void setRandomSeed(int seed) { 79 80 rnd = new Random(seed); 81 } 82 generateRandomAlphaBytes(byte[] bytes)83 static public void generateRandomAlphaBytes(byte[] bytes) { 84 85 byte[] aAndZ = StringUtils.toUTF8("AZ"); 86 int range = aAndZ[1] - aAndZ[0] + 1; 87 88 for (int i = 0; i < bytes.length; i++) { 89 bytes[i] = (byte) (rnd.nextInt(range) + aAndZ[0]); 90 } 91 } 92 checkLatchCount()93 static public void checkLatchCount() { 94 TestCase.assertTrue(LatchSupport.nBtreeLatchesHeld() == 0); 95 } 96 printLatchCount(String msg)97 static public void printLatchCount(String msg) { 98 System.out.println(msg + " : " + LatchSupport.nBtreeLatchesHeld()); 99 } 100 printLatches(String msg)101 static public void printLatches(String msg) { 102 System.out.println(msg + " : "); 103 LatchSupport.dumpBtreeLatchesHeld(); 104 } 105 106 /** 107 * Generate a synthetic base 26 four byte alpha key from an int. 108 * The bytes of the key are between 'A' and 'Z', inclusive. 0 maps 109 * to 'AAAA', 1 to 'AAAB', etc. 110 */ alphaKey(int i)111 static public int alphaKey(int i) { 112 113 int ret = 0; 114 for (int j = 0; j < 4; j++) { 115 byte b = (byte) (i % 26); 116 ret <<= 8; 117 ret |= (b + 65); 118 i /= 26; 119 } 120 121 return ret; 122 } 123 124 /** 125 * Marshall an unsigned int (long) into a four byte buffer. 126 */ putUnsignedInt(byte[] buf, long value)127 static public void putUnsignedInt(byte[] buf, long value) { 128 129 int i = 0; 130 buf[i++] = (byte) (value >>> 0); 131 buf[i++] = (byte) (value >>> 8); 132 buf[i++] = (byte) (value >>> 16); 133 buf[i] = (byte) (value >>> 24); 134 } 135 136 /** 137 * All flavors of removeLogFiles should check if the remove has been 138 * disabled. (Used for debugging, so that the tester can dump the 139 * log file. 140 */ removeDisabled()141 private static boolean removeDisabled() { 142 143 String doRemove = System.getProperty("removeLogFiles"); 144 return ((doRemove != null) && doRemove.equalsIgnoreCase("false")); 145 } 146 147 /** 148 * Remove je log files from the home directory. Will be disabled 149 * if the unit test is run with -DremoveLogFiles=false 150 * @param msg prefix to append to error messages 151 * @param envFile environment directory 152 */ removeLogFiles(String msg, File envFile, boolean checkRemove)153 public static void removeLogFiles(String msg, 154 File envFile, 155 boolean checkRemove) { 156 removeFiles(msg, envFile, FileManager.JE_SUFFIX, checkRemove); 157 removeSubDirs(envFile); 158 } 159 160 /** 161 * Remove files with this suffix from the je home directory 162 * @param msg prefix to append to error messages 163 * @param envFile environment directory 164 * @param suffix files with this suffix will be removed 165 */ removeFiles(String msg, File envFile, String suffix)166 public static void removeFiles(String msg, 167 File envFile, 168 String suffix) { 169 removeFiles(msg, envFile, suffix, false); 170 } 171 172 /** 173 * Remove files with this suffix from the je home directory 174 * @param msg prefix to append to error messages 175 * @param envFile environment directory 176 * @param suffix files with this suffix will be removed 177 * @param checkRemove if true, check the -DremoveLogFiles system 178 * property before removing. 179 */ removeFiles(String msg, File envFile, String suffix, boolean checkRemove)180 public static void removeFiles(String msg, 181 File envFile, 182 String suffix, 183 boolean checkRemove) { 184 if (checkRemove && removeDisabled()) { 185 return; 186 } 187 188 String[] suffixes = new String[] { suffix }; 189 String[] names = FileManager.listFiles(envFile, suffixes, false); 190 191 /* Clean up any target files in this directory. */ 192 for (int i = 0; i < names.length; i++) { 193 File oldFile = new File(envFile, names[i]); 194 boolean done = oldFile.delete(); 195 assert done : 196 msg + " directory = " + envFile + 197 " couldn't delete " + names[i] + " out of " + 198 names[names.length - 1]; 199 oldFile = null; 200 } 201 } 202 203 /** 204 * Remove files with the pattern indicated by the filename filter from the 205 * environment home directory. 206 * Note that BadFileFilter looks for this pattern: NNNNNNNN.bad.# 207 * InfoFileFilter looks for this pattern: je.info.# 208 * @param envFile environment directory 209 */ removeFiles(File envFile, FilenameFilter filter)210 public static void removeFiles(File envFile, FilenameFilter filter) { 211 if (removeDisabled()) { 212 return; 213 } 214 215 File[] targetFiles = envFile.listFiles(filter); 216 217 // Clean up any target files in this directory 218 for (int i = 0; i < targetFiles.length; i++) { 219 boolean done = targetFiles[i].delete(); 220 if (!done) { 221 System.out.println 222 ("Warning, couldn't delete " 223 + targetFiles[i] 224 + " out of " 225 + targetFiles[targetFiles.length - 1]); 226 } 227 } 228 } 229 230 /** 231 * Useful utility for generating byte arrays with a known order. 232 * Vary the length just to introduce more variability. 233 * @return a byte array of length val % 100 with the value of "val" 234 */ getTestArray(int val)235 public static byte[] getTestArray(int val) { 236 237 int length = val % 10; 238 length = length < 4 ? 4 : length; 239 byte[] test = new byte[length]; 240 test[3] = (byte) ((val >>> 0) & 0xff); 241 test[2] = (byte) ((val >>> 8) & 0xff); 242 test[1] = (byte) ((val >>> 16) & 0xff); 243 test[0] = (byte) ((val >>> 24) & 0xff); 244 return test; 245 } 246 247 /** 248 * Return the value of a test data array generated with getTestArray 249 * as an int 250 */ getTestVal(byte[] testArray)251 public static int getTestVal(byte[] testArray) { 252 253 int val = 0; 254 val |= (testArray[3] & 0xff); 255 val |= ((testArray[2] & 0xff) << 8); 256 val |= ((testArray[1] & 0xff) << 16); 257 val |= ((testArray[0] & 0xff) << 24); 258 return val; 259 } 260 261 /** 262 * @return length and data of a byte array, printed as decimal numbers 263 */ dumpByteArray(byte[] b)264 public static String dumpByteArray(byte[] b) { 265 266 StringBuilder sb = new StringBuilder(); 267 sb.append("<byteArray len = "); 268 sb.append(b.length); 269 sb.append(" data = \""); 270 for (int i = 0; i < b.length; i++) { 271 sb.append(b[i]).append(","); 272 } 273 sb.append("\"/>"); 274 return sb.toString(); 275 } 276 277 /** 278 * @return a copy of the passed in byte array 279 */ byteArrayCopy(byte[] ba)280 public static byte[] byteArrayCopy(byte[] ba) { 281 282 int len = ba.length; 283 byte[] ret = new byte[len]; 284 System.arraycopy(ba, 0, ret, 0, len); 285 return ret; 286 } 287 288 /* 289 * Check that the stored memory count for all INs on the inlist 290 * matches their computed count. The environment mem usage check 291 * may be run with assertions or not. 292 * 293 * In a multithreaded environment (or one with daemons running), 294 * you can't be sure that the cached size will equal the calculated size. 295 * 296 * Nodes, txns, and locks are all counted within the memory budget. 297 */ validateNodeMemUsage(EnvironmentImpl envImpl, boolean assertOnError)298 public static long validateNodeMemUsage(EnvironmentImpl envImpl, 299 boolean assertOnError) 300 throws DatabaseException { 301 302 TreeMemTally tally = tallyTreeMemUsage(envImpl); 303 long nodeTallyUsage = tally.treeNodeUsage; 304 long nodeCacheUsage = envImpl.getMemoryBudget().getTreeMemoryUsage(); 305 NumberFormat formatter = NumberFormat.getNumberInstance(); 306 if (assertOnError) { 307 assert (nodeTallyUsage == nodeCacheUsage) : 308 "treeNodeTallyUsage=" + formatter.format(nodeTallyUsage) + 309 " treeNodeCacheUsage=" + formatter.format(nodeCacheUsage); 310 } else { 311 if (DEBUG) { 312 if (nodeCacheUsage != nodeTallyUsage) { 313 double diff = Math.abs(nodeCacheUsage - nodeTallyUsage); 314 if ((diff / nodeCacheUsage) > .05) { 315 System.out.println("treeNodeTallyUsage=" + 316 formatter.format(nodeTallyUsage) + 317 " treeNodeCacheUsage=" + 318 formatter.format(nodeCacheUsage)); 319 } 320 } 321 } 322 } 323 324 long adminTallyUsage = tally.treeAdminUsage; 325 long adminCacheUsage = 326 envImpl.getMemoryBudget().getTreeAdminMemoryUsage(); 327 if (assertOnError) { 328 assert (adminTallyUsage == adminCacheUsage) : 329 "treeAdminTallyUsage=" + formatter.format(adminTallyUsage) + 330 " treeAdminCacheUsage=" + formatter.format(adminCacheUsage); 331 } else { 332 if (DEBUG) { 333 if (adminCacheUsage != adminTallyUsage) { 334 double diff = Math.abs(adminCacheUsage - adminTallyUsage); 335 if ((diff / adminCacheUsage) > .05) { 336 System.out.println("treeAdminTallyUsage=" + 337 formatter.format(adminTallyUsage) + 338 " treeAdminCacheUsage=" + 339 formatter.format(adminCacheUsage)); 340 } 341 } 342 } 343 } 344 345 return nodeCacheUsage; 346 } 347 tallyNodeMemUsage(EnvironmentImpl envImpl)348 public static long tallyNodeMemUsage(EnvironmentImpl envImpl) 349 throws DatabaseException { 350 351 return tallyTreeMemUsage(envImpl).treeNodeUsage; 352 } 353 354 static class TreeMemTally { 355 final long treeNodeUsage; 356 final long treeAdminUsage; 357 TreeMemTally(long treeNodeUsage, long treeAdminUsage)358 TreeMemTally(long treeNodeUsage, long treeAdminUsage) { 359 this.treeNodeUsage = treeNodeUsage; 360 this.treeAdminUsage = treeAdminUsage; 361 } 362 } 363 tallyTreeMemUsage(EnvironmentImpl envImpl)364 private static TreeMemTally tallyTreeMemUsage(EnvironmentImpl envImpl) 365 throws DatabaseException { 366 367 long treeNodeUsage = 0; 368 long treeAdminUsage = envImpl.getDbTree().getTreeAdminMemory(); 369 for (IN in : envImpl.getInMemoryINs()) { 370 in.latch(); 371 try { 372 assert in.verifyMemorySize(): 373 "in nodeId=" + in.getNodeId() + 374 ' ' + in.getClass().getName(); 375 376 treeNodeUsage += in.getBudgetedMemorySize(); 377 378 for (int i = 0; i < in.getNEntries(); i += 1) { 379 Object child = in.getTarget(i); 380 if (child instanceof LN) { 381 treeAdminUsage += ((LN) child).getTreeAdminMemory(); 382 } 383 } 384 } finally { 385 in.releaseLatch(); 386 } 387 } 388 return new TreeMemTally(treeNodeUsage, treeAdminUsage); 389 } 390 391 /** 392 * Called by each unit test to enforce isolation level settings specified 393 * in the isolationLevel system property. Other system properties or 394 * default settings may be applied in the future. 395 */ initEnvConfig()396 public static EnvironmentConfig initEnvConfig() { 397 398 EnvironmentConfig config = new EnvironmentConfig(); 399 String val = System.getProperty("isolationLevel"); 400 if (val != null && val.length() > 0) { 401 if ("serializable".equals(val)) { 402 config.setTxnSerializableIsolation(true); 403 } else if ("readCommitted".equals(val)) { 404 DbInternal.setTxnReadCommitted(config, true); 405 } else { 406 throw new IllegalArgumentException 407 ("Unknown isolationLevel system property value: " + val); 408 } 409 } 410 return config; 411 } 412 413 /** 414 * If a unit test needs to override the isolation level, it should call 415 * this method after calling initEnvConfig. 416 */ clearIsolationLevel(EnvironmentConfig config)417 public static void clearIsolationLevel(EnvironmentConfig config) { 418 DbInternal.setTxnReadCommitted(config, false); 419 config.setTxnSerializableIsolation(false); 420 } 421 422 /** 423 * Loads the given resource relative to the given class, and copies it to 424 * log file zero in the given directory. 425 */ loadLog(Class<?> cls, String resourceName, File envHome)426 public static void loadLog(Class<?> cls, String resourceName, File envHome) 427 throws IOException { 428 429 loadLog(cls, resourceName, envHome, LOG_FILE_NAME); 430 } 431 432 /** 433 * Loads the given resource relative to the given class, and copies it to 434 * the given log file in the given directory. 435 */ loadLog(Class cls, String resourceName, File envHome, String logFileName)436 public static void loadLog(Class cls, 437 String resourceName, 438 File envHome, 439 String logFileName) 440 throws IOException { 441 442 File logFile = new File(envHome, logFileName); 443 InputStream is = cls.getResourceAsStream(resourceName); 444 OutputStream os = new FileOutputStream(logFile); 445 byte[] buf = new byte[is.available()]; 446 int len = is.read(buf); 447 if (buf.length != len) { 448 throw new IllegalStateException(); 449 } 450 os.write(buf, 0, len); 451 is.close(); 452 os.close(); 453 } 454 455 /** 456 * Logs the BIN at the cursor provisionally and the parent IN 457 * non-provisionally. Used to simulate a partial checkpoint or eviction. 458 */ logBINAndIN(Environment env, Cursor cursor)459 public static void logBINAndIN(Environment env, Cursor cursor) 460 throws DatabaseException { 461 462 logBINAndIN(env, cursor, false /*allowDeltas*/); 463 } 464 logBINAndIN(Environment env, Cursor cursor, boolean allowDeltas)465 public static void logBINAndIN(Environment env, 466 Cursor cursor, 467 boolean allowDeltas) 468 throws DatabaseException { 469 470 BIN bin = getBIN(cursor); 471 Tree tree = bin.getDatabase().getTree(); 472 473 /* Log the BIN and update its parent entry. */ 474 bin.latch(); 475 476 SearchResult result = tree.getParentINForChildIN( 477 bin, false, /*useTargetLevel*/ 478 true, CacheMode.DEFAULT); 479 480 assert result.parent != null; 481 assert result.exactParentFound; 482 IN binParent = result.parent; 483 484 long binLsn = logIN(env, bin, allowDeltas, true, binParent); 485 486 binParent.updateNode(result.index, bin, binLsn, 0 /*lastLoggedSize*/); 487 488 result.parent.releaseLatch(); 489 490 /* Log the BIN parent and update its parent entry. */ 491 binParent.latch(); 492 493 if (binParent.isRoot()) { 494 binParent.releaseLatch(); 495 result.parent = null; 496 } else { 497 result = tree.getParentINForChildIN( 498 binParent, false, /*useTargetLevel*/ 499 true, CacheMode.DEFAULT); 500 } 501 502 IN inParent = null; 503 if (result.parent != null) { 504 result.parent.releaseLatch(); 505 assert result.exactParentFound; 506 inParent = result.parent; 507 inParent.latch(); 508 } 509 510 final long inLsn = logIN(env, binParent, allowDeltas, false, null); 511 512 if (inParent != null) { 513 inParent.updateNode( 514 result.index, binParent, inLsn, 0 /*lastLoggedSize*/); 515 516 inParent.releaseLatch(); 517 } else { 518 tree.withRootLatchedExclusive(new WithRootLatched() { 519 public IN doWork(ChildReference root) { 520 root.setLsn(inLsn); 521 return null; 522 } 523 }); 524 } 525 } 526 527 /** 528 * Logs the given IN. 529 */ logIN(Environment env, IN in, boolean allowDeltas, boolean provisional, IN parent)530 public static long logIN(Environment env, 531 IN in, 532 boolean allowDeltas, 533 boolean provisional, 534 IN parent) 535 throws DatabaseException { 536 537 EnvironmentImpl envImpl = DbInternal.getEnvironmentImpl(env); 538 in.latch(); 539 long lsn = in.log(envImpl.getLogManager(), allowDeltas, 540 false /*allowCompress*/, provisional, 541 false /*backgroundIO*/, parent); 542 in.releaseLatch(); 543 return lsn; 544 } 545 546 /** 547 * Returns the parent IN of the given IN. 548 */ getIN(IN in)549 public static IN getIN(IN in) 550 throws DatabaseException { 551 552 Tree tree = in.getDatabase().getTree(); 553 in.latch(); 554 555 SearchResult result = tree.getParentINForChildIN( 556 in, false, /*useTargetLevel*/ 557 true, CacheMode.DEFAULT); 558 559 assert result.parent != null; 560 result.parent.releaseLatch(); 561 assert result.exactParentFound; 562 return result.parent; 563 } 564 565 /** 566 * Returns the target BIN for the given cursor. 567 */ getBIN(Cursor cursor)568 public static BIN getBIN(Cursor cursor) { 569 CursorImpl impl = DbTestProxy.dbcGetCursorImpl(cursor); 570 BIN bin = impl.getBIN(); 571 assert bin != null; 572 return bin; 573 } 574 575 /** 576 * Assert if the tree is not this deep. Use to ensure that data setups 577 * are as expected. 578 */ checkTreeDepth(Database db, int desiredDepth)579 public static boolean checkTreeDepth(Database db, int desiredDepth) 580 throws DatabaseException { 581 582 Tree tree = DbInternal.getDatabaseImpl(db).getTree(); 583 IN rootIN = tree.getRootIN(CacheMode.UNCHANGED); 584 int level = 0; 585 if (rootIN != null) { 586 level = rootIN.getLevel() & IN.LEVEL_MASK; 587 rootIN.releaseLatch(); 588 } 589 590 return (desiredDepth == level); 591 } 592 593 /** 594 * @return true if long running tests are enabled. 595 */ runLongTests()596 static public boolean runLongTests() { 597 return SharedTestUtils.runLongTests(); 598 } 599 600 /** 601 * Skip over the JE version number at the start of the exception 602 * message for tests which are looking for a specific message. 603 */ skipVersion(Exception e)604 public static String skipVersion(Exception e) { 605 final String header = DatabaseException.getVersionHeader(); 606 final String msg = e.getMessage(); 607 if (msg == null || !msg.startsWith(header)) { 608 return msg; 609 } 610 return msg.substring(header.length()); 611 } 612 createEnvHomeWithSubDir(File envHome, int subDirNumber)613 public static void createEnvHomeWithSubDir(File envHome, 614 int subDirNumber) { 615 if (!envHome.exists()) { 616 throw new IllegalStateException 617 ("Environment home directory doesn't exist."); 618 } 619 620 for (int i = 1; i <= subDirNumber; i++) { 621 String fileName = getSubDirName(i); 622 File subDir = new File(envHome, fileName); 623 subDir.mkdir(); 624 } 625 } 626 getSubDirName(int i)627 public static String getSubDirName(int i) { 628 if (i < 10) { 629 return "data00" + i; 630 } else if (i < 100) { 631 return "data0" + i; 632 } else if (i <= 256) { 633 return "data" + i; 634 } else { 635 throw new IllegalArgumentException 636 ("The number of sub directories is invalid."); 637 } 638 } 639 removeSubDirs(File envHome)640 public static void removeSubDirs(File envHome) { 641 if (envHome == null || !envHome.exists()) { 642 return; 643 } 644 645 File[] files = envHome.listFiles(); 646 for (File file : files) { 647 if (file.isDirectory() && file.getName().startsWith("data")) { 648 File[] subFiles = file.listFiles(); 649 for (File subFile : subFiles) { 650 subFile.delete(); 651 } 652 file.delete(); 653 } 654 } 655 } 656 657 /* Read the je.properties and write a new configuration. */ readWriteJEProperties(File envHome, String configure)658 public static ArrayList<String> readWriteJEProperties(File envHome, 659 String configure) 660 throws IOException { 661 662 /* Read the je.properties. */ 663 File propertyFile = new File(envHome, "je.properties"); 664 BufferedReader reader = 665 new BufferedReader(new FileReader(propertyFile)); 666 ArrayList<String> formerLines = new ArrayList<String>(); 667 String line = null; 668 while ((line = reader.readLine()) != null) { 669 formerLines.add(line); 670 } 671 reader.close(); 672 673 /* Write the replicated parameters in the je.properties file. */ 674 FileWriter writer = new FileWriter(propertyFile, true); 675 writer.append(configure + "\n"); 676 writer.flush(); 677 writer.close(); 678 679 return formerLines; 680 } 681 682 /* 683 * Rewrite the je.properties with configurations, it will delete the old 684 * file and rewrite a new one. 685 */ reWriteJEProperties(File envHome, ArrayList<String> formerLines)686 public static void reWriteJEProperties(File envHome, 687 ArrayList<String> formerLines) 688 throws IOException { 689 690 File propertyFile = new File(envHome, "je.properties"); 691 /* Write the je.properties file with the former content. */ 692 if (propertyFile.exists() && propertyFile.isFile()) { 693 TestCase.assertTrue(propertyFile.delete()); 694 } 695 TestCase.assertTrue(!propertyFile.exists()); 696 697 propertyFile = new File(envHome, "je.properties"); 698 TestCase.assertTrue(propertyFile.createNewFile()); 699 700 FileWriter writer = new FileWriter(propertyFile, true); 701 for (String configure : formerLines) { 702 writer.append(configure + "\n"); 703 } 704 writer.flush(); 705 writer.close(); 706 } 707 708 /* Serialize an object and read it again. */ serializeAndReadObject(File envHome, Object object)709 public static Object serializeAndReadObject(File envHome, Object object) 710 throws Exception { 711 712 File output = new File(envHome, "configure.out"); 713 ObjectOutputStream out = 714 new ObjectOutputStream(new FileOutputStream(output)); 715 out.writeObject(object); 716 out.close(); 717 718 if (!output.exists()) { 719 throw new IllegalStateException 720 ("Can't create the output for serialized object."); 721 } 722 723 ObjectInputStream in = 724 new ObjectInputStream(new FileInputStream(output)); 725 Object newObject = in.readObject(); 726 in.close(); 727 728 if (!output.delete()) { 729 throw new IllegalStateException 730 ("Can't delete the output for serialized object after " + 731 "testing is done."); 732 } 733 734 return newObject; 735 } 736 737 /** 738 * Dump any exception messages to stderr. 739 */ 740 public static class StdErrExceptionListener 741 implements ExceptionListener { 742 exceptionThrown(ExceptionEvent event)743 public void exceptionThrown(ExceptionEvent event) { 744 System.err.println(Thread.currentThread() + 745 " received " + 746 event); 747 } 748 } 749 750 /** 751 * Calls Closeable.close for each parameter in the order given, if it is 752 * non-null. 753 * 754 * If one or more close methods throws an Exception, all close methods will 755 * still be called and the first Exception will be rethrown. If an Error 756 * is thrown by a close method, it will be thrown by this method and no 757 * further close methods will be called. An IOException may be thrown by a 758 * close method because is declared by Closeable.close; however, the use of 759 * RuntimeExceptions is recommended. 760 */ closeAll(Closeable... objects)761 public static void closeAll(Closeable... objects) 762 throws Exception { 763 764 closeAll(null, objects); 765 } 766 767 /** 768 * Same as closeAll(Closeable...) but allows passing an initial exception, 769 * when one may have been thrown earlier during a shutdown procedure. If 770 * null is passed for the firstEx parameter, calling this method is 771 * equivalent to calling closeAll(Closeable...). 772 */ closeAll(Exception firstEx, Closeable... objects)773 public static void closeAll(Exception firstEx, Closeable... objects) 774 throws Exception { 775 776 for (Closeable c : objects) { 777 if (c == null) { 778 continue; 779 } 780 try { 781 c.close(); 782 } catch (Exception e) { 783 if (firstEx == null) { 784 firstEx = e; 785 } 786 } 787 } 788 789 if (firstEx != null) { 790 throw firstEx; 791 } 792 } 793 } 794