1 24 25 package com.mckoi.database; 26 27 import com.mckoi.store.LoggingBufferManager; 28 import com.mckoi.util.Stats; 29 import com.mckoi.util.StringUtil; 30 import com.mckoi.util.LogWriter; 31 import com.mckoi.debug.*; 32 import com.mckoi.database.control.DBConfig; 33 import java.io.File ; 34 import java.io.IOException ; 35 import java.io.PrintWriter ; 36 import java.io.Writer ; 37 import java.util.Date ; 38 import java.util.List ; 39 import java.util.ArrayList ; 40 import java.util.Properties ; 41 42 49 50 public class TransactionSystem { 51 52 55 private final Stats stats = new Stats(); 56 57 63 private final DefaultDebugLogger logger; 64 65 69 private DBConfig config = null; 70 71 76 private File db_path; 77 78 81 private boolean lookup_comparison_list_enabled = false; 82 83 87 private boolean read_only_access = false; 88 89 93 private boolean table_lock_check = false; 94 95 100 private boolean soft_index_storage = false; 101 102 107 private boolean always_reindex_dirty_tables = false; 108 109 114 private boolean dont_synch_filesystem = false; 115 116 120 private boolean ignore_case_for_identifiers = false; 121 122 129 private boolean transaction_error_on_dirty_select = true; 130 131 134 private DataCellCache data_cell_cache = null; 135 136 140 private ArrayList function_factory_list; 141 142 146 private DSFunctionLookup function_lookup; 147 148 152 private RegexLibrary regex_library; 153 154 157 private File log_directory; 158 159 165 private LoggingBufferManager buffer_manager; 166 167 171 private StoreSystem store_system; 172 173 175 179 private ArrayList table_listeners; 180 181 182 185 public TransactionSystem() { 186 logger = new DefaultDebugLogger(); 188 Properties p = System.getProperties(); 189 stats.set(0, "Runtime.java.version: " + p.getProperty("java.version")); 190 stats.set(0, "Runtime.java.vendor: " + p.getProperty("java.vendor")); 191 stats.set(0, "Runtime.java.vm.name: " + p.getProperty("java.vm.name")); 192 stats.set(0, "Runtime.os.name: " + p.getProperty("os.name")); 193 stats.set(0, "Runtime.os.arch: " + p.getProperty("os.arch")); 194 stats.set(0, "Runtime.os.version: " + p.getProperty("os.version")); 195 table_listeners = new ArrayList (); 196 } 197 198 203 private static File parseFileString(File root_path, String root_info, 204 String path_string) { 205 File path = new File (path_string); 206 File res; 207 if (path.isAbsolute()) { 209 res = path; 210 } 211 else { 212 if (root_info != null && 214 root_info.equals("jvm")) { 215 return path; 216 } 217 else { 220 res = new File (root_path, path_string); 221 } 222 } 223 return res; 224 } 225 226 229 private void setupLog(DBConfig config) { 230 String log_path_string = config.getValue("log_path"); 231 String root_path_var = config.getValue("root_path"); 232 String read_only_access = config.getValue("read_only"); 233 String debug_logs = config.getValue("debug_logs"); 234 boolean read_only_bool = false; 235 if (read_only_access != null) { 236 read_only_bool = read_only_access.equalsIgnoreCase("enabled"); 237 } 238 boolean debug_logs_bool = true; 239 if (debug_logs != null) { 240 debug_logs_bool = debug_logs.equalsIgnoreCase("enabled"); 241 } 242 243 247 if (debug_logs_bool && !read_only_bool && 248 log_path_string != null && !log_path_string.equals("")) { 249 File log_path = parseFileString(config.currentPath(), root_path_var, 251 log_path_string); 252 if (!log_path.exists()) { 254 log_path.mkdirs(); 255 } 256 setLogDirectory(log_path); 258 259 LogWriter f_writer; 260 File debug_log_file; 261 String dlog_file_name = ""; 262 try { 263 dlog_file_name = config.getValue("debug_log_file"); 264 debug_log_file = new File (log_path.getCanonicalPath(), 265 dlog_file_name); 266 267 f_writer = new LogWriter(debug_log_file, 512 * 1024, 12); 269 f_writer.write("**** Debug log started: " + 270 new Date (System.currentTimeMillis()) + " ****\n"); 271 f_writer.flush(); 272 } 273 catch (IOException e) { 274 throw new RuntimeException ( 275 "Unable to open debug file '" + dlog_file_name + 276 "' in path '" + log_path + "'"); 277 } 278 setDebugOutput(f_writer); 279 } 280 281 if (!debug_logs_bool) { 283 setDebugOutput(new PrintWriter (new Writer() { 287 public void write(int c) throws IOException { 288 } 289 public void write(char cbuf[], int off, int len) throws IOException { 290 } 291 public void write(String str, int off, int len) throws IOException { 292 } 293 public void flush() throws IOException { 294 } 295 public void close() throws IOException { 296 } 297 })); 298 } 299 300 int debug_level = Integer.parseInt(config.getValue("debug_level")); 301 if (debug_level == -1) { 302 setDebugLevel(255); 303 } 304 else { 305 setDebugLevel(debug_level); 306 } 307 } 308 309 312 public final String getConfigString(String property, String default_val) { 313 String v = config.getValue(property); 314 if (v == null) { 315 return default_val; 316 } 317 return v.trim(); 318 } 319 320 323 public final int getConfigInt(String property, int default_val) { 324 String v = config.getValue(property); 325 if (v == null) { 326 return default_val; 327 } 328 return Integer.parseInt(v); 329 } 330 331 334 public final boolean getConfigBoolean(String property, boolean default_val) { 335 String v = config.getValue(property); 336 if (v == null) { 337 return default_val; 338 } 339 return v.trim().equalsIgnoreCase("enabled"); 340 } 341 342 343 348 private static String regexStringToClass(String lib) { 349 if (lib.equals("java.util.regexp")) { 350 return "com.mckoi.database.regexbridge.JavaRegex"; 351 } 352 else if (lib.equals("org.apache.regexp")) { 353 return "com.mckoi.database.regexbridge.ApacheRegex"; 354 } 355 else if (lib.equals("gnu.regexp")) { 356 return "com.mckoi.database.regexbridge.GNURegex"; 357 } 358 else { 359 return null; 360 } 361 } 362 363 368 public void init(DBConfig config) { 369 370 function_factory_list = new ArrayList (); 371 function_lookup = new DSFunctionLookup(); 372 373 if (config != null) { 374 this.config = config; 375 376 read_only_access = getConfigBoolean("read_only", false); 378 379 setupLog(config); 381 382 String storage_system = getConfigString("storage_system", "v1file"); 384 385 boolean is_file_store_mode; 386 387 if (storage_system.equalsIgnoreCase("v1file")) { 389 Debug().write(Lvl.MESSAGE, this, 390 "Storage System: v1 file storage mode."); 391 392 String database_path = getConfigString("database_path", "./data"); 394 String root_path_var = getConfigString("root_path", "jvm"); 396 397 db_path = parseFileString(config.currentPath(), root_path_var, 399 database_path); 400 401 store_system = new V1FileStoreSystem(this, db_path, read_only_access); 402 is_file_store_mode = true; 403 } 404 405 else if (storage_system.equalsIgnoreCase("v1javaheap")) { 406 Debug().write(Lvl.MESSAGE, this, 407 "Storage System: v1 Java heap storage mode."); 408 store_system = new V1HeapStoreSystem(); 409 is_file_store_mode = false; 410 } 411 412 else { 413 String error_msg = "Unknown storage_system property: " + storage_system; 414 Debug().write(Lvl.ERROR, this, error_msg); 415 throw new RuntimeException (error_msg); 416 } 417 418 addFunctionFactory(new InternalFunctionFactory()); 420 421 String status; 422 423 int max_cache_size = 0, max_cache_entry_size = 0; 425 426 max_cache_size = getConfigInt("data_cache_size", 0); 427 max_cache_entry_size = getConfigInt("max_cache_entry_size", 0); 428 429 if (max_cache_size >= 4096 && 430 max_cache_entry_size >= 16 && 431 max_cache_entry_size < (max_cache_size / 2)) { 432 433 Debug().write(Lvl.MESSAGE, this, 434 "Internal Data Cache size: " + max_cache_size); 435 Debug().write(Lvl.MESSAGE, this, 436 "Internal Data Cache max cell size: " + max_cache_entry_size); 437 438 int hash_size = DataCellCache.closestPrime(max_cache_size / 55); 440 441 data_cell_cache = new DataCellCache(this, 443 max_cache_size, max_cache_entry_size, hash_size); 444 445 } 446 else { 447 Debug().write(Lvl.MESSAGE, this, 448 "Internal Data Cache disabled."); 449 } 450 451 lookup_comparison_list_enabled = false; 455 Debug().write(Lvl.MESSAGE, this, 456 "lookup_comparison_list = " + lookup_comparison_list_enabled); 457 458 Debug().write(Lvl.MESSAGE, this, 460 "read_only = " + read_only_access); 461 if (read_only_access) stats.set(1, "DatabaseSystem.read_only"); 462 463 470 transaction_error_on_dirty_select = 472 getConfigBoolean("transaction_error_on_dirty_select", true); 473 Debug().write(Lvl.MESSAGE, this, "transaction_error_on_dirty_select = " + 474 transaction_error_on_dirty_select); 475 476 ignore_case_for_identifiers = 478 getConfigBoolean("ignore_case_for_identifiers", false); 479 Debug().write(Lvl.MESSAGE, this, 480 "ignore_case_for_identifiers = " + ignore_case_for_identifiers); 481 482 484 if (is_file_store_mode) { 487 boolean nio_interface_available; 488 try { 489 Class.forName("java.nio.channels.FileChannel"); 490 nio_interface_available = true; 491 Debug().write(Lvl.MESSAGE, this, 492 "Java NIO API is available."); 493 } 494 catch (ClassNotFoundException e) { 495 nio_interface_available = false; 496 Debug().write(Lvl.MESSAGE, this, 497 "Java NIO API is not available."); 498 } 499 500 boolean nio_bugged_os; 503 String os_name = System.getProperties().getProperty("os.name"); 504 nio_bugged_os = (os_name.equalsIgnoreCase("Windows 95") || 505 os_name.equalsIgnoreCase("Windows 98")); 506 507 int io_safety_level = getConfigInt("io_safety_level", 10); 510 if (io_safety_level < 1 || io_safety_level > 10) { 511 Debug().write(Lvl.MESSAGE, this, 512 "Invalid io_safety_level value. Setting to the most safe level."); 513 io_safety_level = 10; 514 } 515 Debug().write(Lvl.MESSAGE, this, 516 "io_safety_level = " + io_safety_level); 517 518 boolean enable_logging = true; 520 if (io_safety_level <= 2) { 521 Debug().write(Lvl.MESSAGE, this, 522 "Disabling journaling and file sync."); 523 enable_logging = false; 524 } 525 526 boolean use_nio_if_available = 529 getConfigBoolean("use_nio_if_available", false); 530 boolean force_use_nio = getConfigBoolean("force_use_nio", false); 531 532 String api_to_use; 533 int page_size; 534 int max_pages; 535 536 final boolean disable_nio = true; 537 538 if ( !disable_nio && 542 ( force_use_nio || 543 ( nio_interface_available && 544 use_nio_if_available && 545 !nio_bugged_os ))) { 546 Debug().write(Lvl.MESSAGE, this, 547 "Using NIO API for OS memory mapped file access."); 548 page_size = getConfigInt("buffered_nio_page_size", 1024 * 1024); 549 max_pages = getConfigInt("buffered_nio_max_pages", 64); 550 api_to_use = "Java NIO"; 551 } 552 else { 553 Debug().write(Lvl.MESSAGE, this, 554 "Using stardard IO API for heap buffered file access."); 555 page_size = getConfigInt("buffered_io_page_size", 8192); 556 max_pages = getConfigInt("buffered_io_max_pages", 256); 557 api_to_use = "Java IO"; 558 } 559 560 Debug().write(Lvl.MESSAGE, this, 562 "[Buffer Manager] Using IO API: " + api_to_use); 563 Debug().write(Lvl.MESSAGE, this, 564 "[Buffer Manager] Page Size: " + page_size); 565 Debug().write(Lvl.MESSAGE, this, 566 "[Buffer Manager] Max pages: " + max_pages); 567 568 final File journal_path = db_path; 570 final long max_slice_size = 16384 * 65536; 572 final String first_file_ext = "koi"; 574 575 buffer_manager = new LoggingBufferManager( 577 db_path, journal_path, read_only_access, max_pages, page_size, 578 first_file_ext, max_slice_size, Debug(), enable_logging); 579 583 try { 585 buffer_manager.start(); 586 } 587 catch (IOException e) { 588 Debug().write(Lvl.ERROR, this, "Error starting buffer manager"); 589 Debug().writeException(Lvl.ERROR, e); 590 throw new Error ("IO Error: " + e.getMessage()); 591 } 592 593 } 594 595 599 boolean regex_api_exists; 601 try { 602 Class.forName("java.util.regex.Pattern"); 603 regex_api_exists = true; 604 } 605 catch (ClassNotFoundException e) { 606 regex_api_exists = false; 608 Debug().write(Lvl.MESSAGE, this, 609 "Java regex API not available."); 610 } 611 612 String regex_bridge; 613 String lib_used; 614 615 String force_lib = getConfigString("force_regex_library", null); 616 617 if (force_lib != null) { 619 lib_used = force_lib; 620 regex_bridge = regexStringToClass(force_lib); 622 } 623 else { 624 String lib = getConfigString("regex_library", null); 625 lib_used = lib; 626 if (regex_api_exists) { 628 regex_bridge = "com.mckoi.database.regexbridge.JavaRegex"; 629 } 630 else if (lib != null) { 631 regex_bridge = regexStringToClass(lib); 633 } 634 else { 635 regex_bridge = null; 636 } 637 } 638 639 if (regex_bridge != null) { 640 try { 641 Class c = Class.forName(regex_bridge); 642 regex_library = (RegexLibrary) c.newInstance(); 643 Debug().write(Lvl.MESSAGE, this, 644 "Using regex bridge: " + lib_used); 645 } 646 catch (Throwable e) { 647 Debug().write(Lvl.ERROR, this, 648 "Unable to load regex bridge: " + regex_bridge); 649 Debug().writeException(Lvl.WARNING, e); 650 } 651 } 652 else { 653 if (lib_used != null) { 654 Debug().write(Lvl.ERROR, this, 655 "Regex library not known: " + lib_used); 656 } 657 Debug().write(Lvl.MESSAGE, this, 658 "Regex features disabled."); 659 } 660 661 663 try { 664 String function_factories = 666 getConfigString("function_factories", null); 667 if (function_factories != null) { 668 List factories = StringUtil.explode(function_factories, ";"); 669 for (int i = 0; i < factories.size(); ++i) { 670 String factory_class = factories.get(i).toString(); 671 Class c = Class.forName(factory_class); 672 FunctionFactory fun_factory = (FunctionFactory) c.newInstance(); 673 addFunctionFactory(fun_factory); 674 Debug().write(Lvl.MESSAGE, this, 675 "Successfully added function factory: " + factory_class); 676 } 677 } 678 else { 679 Debug().write(Lvl.MESSAGE, this, 680 "No 'function_factories' config property found."); 681 } 683 } 684 catch (Throwable e) { 685 Debug().write(Lvl.ERROR, this, 686 "Error parsing 'function_factories' configuration property."); 687 Debug().writeException(e); 688 } 689 690 flushCachedFunctionLookup(); 692 693 } 694 695 } 696 697 701 public void setupRowCache(int max_cache_size, 702 int max_cache_entry_size) { 703 data_cell_cache = 705 new DataCellCache(this, max_cache_size, max_cache_entry_size); 706 } 707 708 712 public boolean readOnlyAccess() { 713 return read_only_access; 714 } 715 716 722 public File getDatabasePath() { 723 return db_path; 724 } 725 726 729 public boolean tableLockingEnabled() { 730 return table_lock_check; 731 } 732 733 737 public boolean lookupComparisonListEnabled() { 738 return lookup_comparison_list_enabled; 739 } 740 741 745 public boolean softIndexStorage() { 746 return soft_index_storage; 747 } 748 749 752 public boolean alwaysReindexDirtyTables() { 753 return always_reindex_dirty_tables; 754 } 755 756 760 public boolean dontSynchFileSystem() { 761 return dont_synch_filesystem; 762 } 763 764 768 public boolean transactionErrorOnDirtySelect() { 769 return transaction_error_on_dirty_select; 770 } 771 772 776 public boolean ignoreIdentifierCase() { 777 return ignore_case_for_identifiers; 778 } 779 780 785 public LoggingBufferManager getBufferManager() { 786 return buffer_manager; 787 } 788 789 792 public RegexLibrary getRegexLibrary() { 793 if (regex_library != null) { 794 return regex_library; 795 } 796 throw new Error ("No regular expression library found in classpath " + 797 "and/or in configuration file."); 798 } 799 800 802 805 public final StoreSystem storeSystem() { 806 return store_system; 807 } 808 809 811 814 public final void setDebugOutput(java.io.Writer writer) { 815 logger.setOutput(writer); 818 } 819 820 823 public final void setDebugLevel(int level) { 824 logger.setDebugLevel(level); 825 } 826 827 831 public final DebugLogger Debug() { 832 return logger; 833 } 834 835 837 843 public void addFunctionFactory(FunctionFactory factory) { 844 synchronized (function_factory_list) { 845 function_factory_list.add(factory); 846 } 847 factory.init(); 848 } 849 850 855 public void flushCachedFunctionLookup() { 856 FunctionFactory[] factories; 857 synchronized (function_factory_list) { 858 factories = (FunctionFactory[]) function_factory_list.toArray( 859 new FunctionFactory[function_factory_list.size()]); 860 } 861 function_lookup.flushContents(factories); 862 } 863 864 871 public FunctionLookup getFunctionLookup() { 872 return function_lookup; 873 } 874 875 877 884 public Transaction.CheckExpression prepareTransactionCheckConstraint( 885 DataTableDef table_def, Transaction.CheckExpression check) { 886 887 Expression exp = check.expression; 891 table_def.resolveColumns(ignoreIdentifierCase(), exp); 892 901 return check; 902 } 903 904 906 910 public final Stats stats() { 911 return stats; 912 } 913 914 916 921 public final void setLogDirectory(File log_path) { 922 this.log_directory = log_path; 923 } 924 925 928 public final File getLogDirectory() { 929 return log_directory; 930 } 931 932 934 939 DataCellCache getDataCellCache() { 940 return data_cell_cache; 941 } 942 943 945 948 private DatabaseDispatcher dispatcher; 949 950 953 private DatabaseDispatcher getDispatcher() { 954 synchronized (this) { 955 if (dispatcher == null) { 956 dispatcher = new DatabaseDispatcher(this); 957 } 958 return dispatcher; 959 } 960 } 961 962 971 Object createEvent(Runnable runnable) { 972 return getDispatcher().createEvent(runnable); 973 } 974 975 981 void postEvent(int time_to_wait, Object event) { 982 getDispatcher().postEvent(time_to_wait, event); 983 } 984 985 986 989 public void dispose() { 990 if (buffer_manager != null) { 991 try { 992 store_system.setCheckPoint(); 994 buffer_manager.stop(); 996 } 997 catch (IOException e) { 998 System.out.println("Error stopping buffer manager."); 999 e.printStackTrace(); 1000 } 1001 } 1002 buffer_manager = null; 1003 regex_library = null; 1004 data_cell_cache = null; 1005 config = null; 1006 log_directory = null; 1007 function_factory_list = null; 1008 store_system = null; 1009 if (dispatcher != null) { 1010 dispatcher.finish(); 1011 } 1012 dispatcher = null; 1014 } 1015 1016 1017 1019 1023 private static class DSFunctionLookup implements FunctionLookup { 1024 1025 private FunctionFactory[] factories; 1026 1027 public synchronized Function generateFunction(FunctionDef function_def) { 1028 for (int i = 0; i < factories.length; ++i) { 1029 Function f = factories[i].generateFunction(function_def); 1030 if (f != null) { 1031 return f; 1032 } 1033 } 1034 return null; 1035 } 1036 1037 public synchronized boolean isAggregate(FunctionDef function_def) { 1038 for (int i = 0; i < factories.length; ++i) { 1039 FunctionInfo f_info = 1040 factories[i].getFunctionInfo(function_def.getName()); 1041 if (f_info != null) { 1042 return f_info.getType() == FunctionInfo.AGGREGATE; 1043 } 1044 } 1045 return false; 1046 } 1047 1048 public synchronized void flushContents(FunctionFactory[] factories) { 1049 this.factories = factories; 1050 } 1051 1052 } 1053 1054} 1055 | Popular Tags |