1 30 31 32 package org.hsqldb.persist; 33 34 import java.io.DataInputStream ; 35 import java.io.File ; 36 import java.io.FileInputStream ; 37 import java.io.RandomAccessFile ; 38 39 import org.hsqldb.DatabaseManager; 40 import org.hsqldb.HsqlDateTime; 41 import org.hsqldb.HsqlException; 42 import org.hsqldb.Trace; 43 import org.hsqldb.lib.FileUtil; 44 import org.hsqldb.lib.HsqlTimer; 45 import org.hsqldb.lib.java.JavaSystem; 46 47 156 public class LockFile { 157 158 159 protected File f; 160 161 162 private String cpath = null; 163 164 171 protected RandomAccessFile raf; 172 173 177 public static final long HEARTBEAT_INTERVAL = 10000; 178 179 183 public static final byte[] MAGIC = "HSQLLOCK".getBytes(); 184 185 186 protected boolean locked; 187 188 192 protected static final HsqlTimer timer = DatabaseManager.getTimer(); 193 194 197 private Object timerTask; 198 199 222 private void checkHeartbeat() throws Exception { 223 224 long lastHeartbeat; 225 String mn; 226 String path; 227 228 mn = "checkHeartbeat(): "; 229 path = "lock file [" + cpath + "]"; 230 231 trace(mn + "entered."); 232 233 if (!f.exists()) { 234 trace(mn + path + " does not exist. Check OK."); 235 236 return; 237 } 238 239 if (f.length() != 16) { 240 trace(mn + path + " length != 16; Check OK."); 241 242 return; 243 } 244 245 try { 246 lastHeartbeat = System.currentTimeMillis() - readHeartbeat(); 247 } catch (Exception e) { 248 249 throw new Exception ( 251 Trace.getMessage( 252 Trace.LockFile_checkHeartbeat, true, new Object [] { 253 e.toString(), cpath 254 })); 255 } 256 257 trace(mn + path + " last heartbeat " + lastHeartbeat + " ms ago."); 258 259 if (Math.abs(lastHeartbeat) < HEARTBEAT_INTERVAL) { 260 throw new Exception ( 261 Trace.getMessage( 262 Trace.LockFile_checkHeartbeat2, true, new Object [] { 263 mn, path 264 })); 265 } 266 } 267 268 273 private void closeRAF() throws Exception { 274 275 String mn; 276 277 mn = "closeRAF(): "; 278 279 trace(mn + "entered."); 280 281 if (raf == null) { 282 trace(mn + "raf was null upon entry. Exiting immediately."); 283 } else { 284 trace(mn + "closing " + raf); 285 raf.close(); 286 trace(mn + raf + " closed successfully. Setting raf null"); 287 288 raf = null; 289 } 290 } 291 292 303 private void setFile(File file) throws Exception { 304 305 if (isLocked()) { 306 try { 307 tryRelease(); 308 } catch (Exception e) { 309 trace(e); 310 } 311 } 312 313 f = FileUtil.canonicalFile(file); 314 cpath = f.getPath(); 315 raf = null; 316 locked = false; 317 } 318 319 358 protected boolean lockImpl() throws Exception { 359 360 String mn; 361 362 mn = "lockImpl(): "; 363 364 trace(mn + "entered."); 365 FileUtil.deleteOnExit(f); 366 367 return true; 368 } 369 370 375 private void openRAF() throws Exception { 376 377 trace("openRAF(): entered."); 378 379 raf = new RandomAccessFile (f, "rw"); 380 381 trace("openRAF(): got new 'rw' mode " + raf); 382 } 383 384 397 private long readHeartbeat() throws Exception { 398 399 DataInputStream dis; 400 long heartbeat; 401 402 heartbeat = Long.MIN_VALUE; 403 404 String mn = "readHeartbeat(): "; 405 String path = "lock file [" + cpath + "]"; 406 407 trace(mn + "entered."); 408 409 if (!f.exists()) { 410 trace(mn + path + " does not exist. Return '" + heartbeat + "'"); 411 412 return heartbeat; 413 } 414 415 dis = new DataInputStream (new FileInputStream (f)); 416 417 trace(mn + " got new " + dis); 418 419 for (int i = 0; i < MAGIC.length; i++) { 420 if (MAGIC[i] != dis.readByte()) { 421 trace(mn + path + " is not lock file. Return '" + heartbeat 422 + "'"); 423 424 return heartbeat; 425 } 426 } 427 428 heartbeat = dis.readLong(); 429 430 trace(mn + " read: [" + HsqlDateTime.getTimestampString(heartbeat) 431 + "]"); 432 dis.close(); 433 trace(mn + " closed " + dis); 434 435 return heartbeat; 436 } 437 438 450 protected boolean releaseImpl() throws Exception { 451 452 trace("releaseImpl(): no action: returning true"); 453 454 return true; 455 } 456 457 458 private void startHeartbeat() { 459 460 Runnable r; 461 462 trace("startHeartbeat(): entered."); 463 464 if (timerTask == null || HsqlTimer.isCancelled(timerTask)) { 465 r = new HeartbeatRunner(); 466 467 timerTask = timer.schedulePeriodicallyAfter(0, 469 HEARTBEAT_INTERVAL, r, true); 470 471 trace("startHeartbeat(): heartbeat task scheduled."); 472 } 473 474 trace("startHeartbeat(): exited."); 475 } 476 477 478 private void stopHeartbeat() { 479 480 String mn = "stopHeartbeat(): "; 481 482 trace(mn + "entered"); 483 484 if (timerTask != null &&!HsqlTimer.isCancelled(timerTask)) { 485 HsqlTimer.cancel(timerTask); 486 487 timerTask = null; 488 } 489 490 trace(mn + "exited"); 491 } 492 493 500 private void writeMagic() throws Exception { 501 502 String mn = "writeMagic(): "; 503 String path = "lock file [" + cpath + "]"; 504 505 trace(mn + "entered."); 506 trace(mn + "raf.seek(0)"); 507 raf.seek(0); 508 trace(mn + "raf.write(byte[])"); 509 raf.write(MAGIC); 510 trace(mn + "wrote [\"HSQLLOCK\".getBytes()] to " + path); 511 } 512 513 520 private void writeHeartbeat() throws Exception { 521 522 long time; 523 String mn = "writeHeartbeat(): "; 524 String path = "lock file [" + cpath + "]"; 525 526 trace(mn + "entered."); 527 528 time = System.currentTimeMillis(); 529 530 trace(mn + "raf.seek(" + MAGIC.length + ")"); 531 raf.seek(MAGIC.length); 532 trace(mn + "raf.writeLong(" + time + ")"); 533 raf.writeLong(time); 534 trace(mn + "wrote [" + time + "] to " + path); 535 } 536 537 549 public static LockFile newLockFile(String path) throws Exception { 550 551 File f; 552 LockFile lf; 553 Class c; 554 555 c = null; 556 557 try { 558 Class.forName("java.nio.channels.FileLock"); 559 560 c = Class.forName("org.hsqldb.persist.NIOLockFile"); 561 lf = (LockFile) c.newInstance(); 562 } catch (Exception e) { 563 lf = new LockFile(); 564 } 565 566 f = new File (path); 567 568 FileUtil.makeParentDirectories(f); 569 lf.setFile(f); 570 571 return lf; 572 } 573 574 public static LockFile newLockFileLock(String path) throws HsqlException { 575 576 LockFile lf = null; 577 578 try { 579 lf = LockFile.newLockFile(path + ".lck"); 580 } catch (Exception e) { 581 throw Trace.error(Trace.FILE_IO_ERROR, e.toString()); 582 } 583 584 boolean locked = false; 585 String msg = ""; 586 587 try { 588 locked = lf.tryLock(); 589 } catch (Exception e) { 590 591 msg = e.toString(); 593 } 594 595 if (!locked) { 596 throw Trace.error(Trace.DATABASE_ALREADY_IN_USE, lf + ": " + msg); 597 } 598 599 return lf; 600 } 601 602 624 public boolean equals(Object obj) { 625 626 if (this == obj) { 628 return true; 629 } else if (obj instanceof LockFile) { 630 LockFile that = (LockFile) obj; 631 632 return (f == null) ? that.f == null 633 : f.equals(that.f); 634 } else { 635 return false; 636 } 637 } 638 639 644 public String getCanonicalPath() { 645 return cpath; 646 } 647 648 660 public int hashCode() { 661 return f == null ? 0 662 : f.hashCode(); 663 } 664 665 690 public boolean isLocked() { 691 return locked; 692 } 693 694 702 public static boolean isLocked(String path) { 703 704 FileInputStream fis = null; 705 706 try { 707 LockFile lf = LockFile.newLockFile(path); 708 709 lf.checkHeartbeat(); 710 711 if (lf.f.exists() && lf.f.isFile()) { 712 fis = new FileInputStream (lf.f); 713 714 fis.read(); 715 } 716 717 return false; 718 } catch (Exception e) {} 719 finally { 720 if (fis != null) { 721 try { 722 fis.close(); 723 } catch (java.io.IOException e) {} 724 } 725 } 726 727 return true; 728 } 729 730 745 public boolean isValid() { 746 return isLocked() && f != null && f.exists() && raf != null; 747 } 748 749 755 protected class HeartbeatRunner implements Runnable { 756 757 public void run() { 758 759 try { 760 trace("HeartbeatRunner.run(): writeHeartbeat()"); 761 writeHeartbeat(); 762 } catch (Throwable t) { 763 trace("HeartbeatRunner.run(): caught Throwable: " + t); 764 } 765 } 766 } 767 768 785 public String toString() { 786 787 return super.toString() + "[file =" + cpath + ", exists=" 788 + f.exists() + ", locked=" + isLocked() + ", valid=" 789 + isValid() + ", " + toStringImpl() + "]"; 790 } 791 792 800 protected String toStringImpl() { 801 return ""; 802 } 803 804 815 public boolean tryLock() throws Exception { 816 817 String mn = "tryLock(): "; 818 819 trace(mn + "entered."); 820 821 if (locked) { 822 trace(mn + " lock already held. Returning true immediately."); 823 824 return true; 825 } 826 827 checkHeartbeat(); 828 829 openRAF(); 843 844 locked = lockImpl(); 845 846 if (locked) { 847 writeMagic(); 848 startHeartbeat(); 849 850 try { 851 852 JavaSystem.runFinalizers(); 856 trace(mn + "success for System.runFinalizersOnExit(true)"); 857 } catch (Exception e) { 858 trace(mn + e.toString()); 859 } 860 } else { 861 try { 862 releaseImpl(); 863 closeRAF(); 864 } catch (Exception e) { 865 trace(mn + e.toString()); 866 } 867 } 868 869 trace(mn + "ran to completion. Returning " + locked); 870 871 return locked; 872 } 873 874 886 public boolean tryRelease() throws Exception { 887 888 String mn = "tryRelease(): "; 889 String path; 890 891 trace(mn + "entered."); 892 893 boolean released = !locked; 894 895 if (released) { 896 trace(mn + "No lock held. Returning true immediately"); 897 898 return true; 899 } 900 901 try { 902 released = releaseImpl(); 903 } catch (Exception e) { 904 trace(mn + e); 905 } 906 907 if (!released) { 908 trace(mn + "releaseImpl() failed. Returning false immediately."); 909 910 return false; 911 } 912 913 trace(mn + "releaseImpl() succeeded."); 914 stopHeartbeat(); 915 closeRAF(); 916 917 trace(mn + "Starting Thread.sleep(100)."); 921 922 try { 923 Thread.sleep(100); 924 } catch (Exception e) { 925 trace(mn + e.toString()); 926 } 927 928 trace(mn + "Finished Thread.sleep(100)."); 929 930 path = "[" + cpath + "]"; 931 932 if (f.exists()) { 933 trace(mn + path + " exists."); 934 935 released = f.delete(); 936 937 trace(mn + path + (released ? "" 938 : "not") + " deleted."); 939 940 if (f.exists()) { 941 trace(mn + " WARNING!: " + path + "still exists."); 942 } 943 } 944 945 locked = !released; 946 947 trace(mn + "ran to completion. Returning " + released); 948 949 return released; 950 } 951 952 957 protected void trace(Object o) { 958 959 if (Trace.TRACE) { 960 Trace.printSystemOut("[" + super.toString() + "]: " + o); 961 } 962 } 963 964 972 protected void finalize() throws Throwable { 973 trace("finalize(): calling tryRelease()"); 974 tryRelease(); 975 } 976 } 977 | Popular Tags |