001 /* 002 * CRIMSON 003 * Copyright (c) 2006, Stephen Fisher, Susan Davidson, and Junhyong Kim, 004 * University of Pennsylvania. 005 * 006 * This program is free software; you can redistribute it and/or 007 * modify it under the terms of the GNU General Public License as 008 * published by the Free Software Foundation; either version 2 of the 009 * License, or (at your option) any later version. 010 * 011 * This program is distributed in the hope that it will be useful, but 012 * WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014 * General Public License for more details. 015 * 016 * You should have received a copy of the GNU General Public License 017 * along with this program; if not, write to the Free Software 018 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 019 * 02110-1301 USA. 020 * 021 * @(#)CrimsonMain.java 022 */ 023 024 package edu.upenn.crimson; 025 026 import edu.upenn.crimson.io.SQL; 027 import org.python.util.InteractiveConsole; 028 import org.python.core.*; 029 import javax.swing.UIManager; 030 import java.util.prefs.*; 031 import java.sql.*; 032 import java.io.*; 033 import java.net.*; 034 035 //import oracle.net.ns.*; 036 //import oracle.net.ano.*; 037 //import oracle.jdbc.*; 038 //import oracle.jdbc.pool.*; 039 040 /** 041 * CrimsonMain program. 042 * 043 * @author Stephen Fisher 044 * @version $Id: CrimsonMain.java,v 1.57 2009/07/25 01:05:46 fisher Exp $ 045 */ 046 047 public class CrimsonMain { 048 public static final InteractiveConsole console = new InteractiveConsole(); 049 public static final Runtime runtime = Runtime.getRuntime(); 050 051 /** Dos interpreter. */ 052 public static boolean ISDOS = false; 053 054 /** Batch mode. */ 055 public static boolean ISBATCH = false; 056 057 /** Connect to Walrus. */ 058 public static boolean WALRUS = false; 059 060 /** No X11 or similar windowing environment. */ 061 public static boolean NOX = false; 062 063 /** This is a reference to the actual database connection. */ 064 private static Connection dbConnection = null; 065 066 /** 067 * Built-in default property values. 068 */ 069 // public static Preferences systemDefaults = Preferences.systemRoot().node("/edu/upenn/crimson"); 070 071 /** User established properties. */ 072 // public static Preferences userDefaults = Preferences.userRoot().node("/edu/upenn/crimson"); 073 public static Preferences userDefaults = Preferences.userRoot().node(".crimson_prefs"); 074 075 /** 076 * @param args Command line arguments. 077 */ 078 public static void main(String[] args) throws PyException { 079 try { 080 // use the local (platform-dependent) look and feel for the GUI 081 UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); 082 } catch (Exception e) { 083 CrimsonUtils.printError("Unable to set LookAndFeel"); 084 e.printStackTrace(); 085 } 086 087 // System.out.println("Hello, brave new world"); 088 // console.push("print sys"); 089 // console.set("a", new PyInteger(42)); 090 // console.push("print a"); 091 // console.push("x = 2 + 2"); 092 // PyObject x = console.get("x"); 093 // System.out.println("x: " + x); 094 // console.push("print a"); 095 096 // setup some convenient references 097 console.set("treePool", ObjectHandles.getTreePool()); 098 console.set("partitionPool", ObjectHandles.getPartitionPool()); 099 console.set("modelPool", ObjectHandles.getModelPool()); 100 console.set("queryPool", ObjectHandles.getQueryPool()); 101 102 // console.push("print"); 103 console.push("print 'CRIMSON v2.1 beta'"); 104 console.push("print 'By Stephen Fisher, Susan Davidson, and Junhyong Kim.'"); 105 console.push("print 'Copyright 2006, University of Pennsylvania. All rights reserved.'"); 106 console.push("print '" + console.getDefaultBanner() + "'"); 107 console.push("print "); 108 109 try { 110 // process the built-in command line arguments 111 for (int i = 0; i < args.length; i++) { 112 // check for the DOS flag 113 if (args[i].compareToIgnoreCase("-DOS") == 0) { 114 ISDOS = true; 115 console.set("ISDOS", "1"); 116 continue; 117 } 118 119 // check for the BATCH flag 120 if (args[i].compareToIgnoreCase("-BATCH") == 0) { 121 ISBATCH = true; 122 continue; 123 } 124 125 // check for the -nox flag (no X11) 126 if (args[i].compareToIgnoreCase("-nox") == 0) { 127 NOX = true; 128 console.set("NOX", "1"); 129 continue; 130 } 131 // check for the -walrus flag 132 if (args[i].compareToIgnoreCase("-walrus") == 0) { 133 WALRUS = true; 134 console.set("WALRUS", "1"); 135 continue; 136 } 137 } 138 139 console.push("print 'Loading startup scripts...'"); 140 // load user-defined functions into the console. 141 console.exec("from startup import *"); 142 143 // process the user-defined command line arguments 144 for (int i = 0; i < args.length; i++) { 145 // skip the built-in flags 146 if (args[i].compareToIgnoreCase("-DOS") == 0) continue; 147 if (args[i].compareToIgnoreCase("-BATCH") == 0) continue; 148 if (args[i].compareToIgnoreCase("-WALRUS") == 0) continue; 149 if (args[i].compareToIgnoreCase("-NOX") == 0) continue; 150 151 CrimsonUtils.printMsg("Running command line arg: " + args[i]); 152 console.push("loadPython(\"" + args[i] + "\")"); 153 } 154 155 } catch (Exception e) { 156 System.err.println(" ** ERROR: Error loading startup script."); 157 System.err.println(" ** ERROR: Startup script not loaded."); 158 159 // XXX Should turn on the Stack dumping with a flag when 160 // running the program. 161 162 // dump stack because getMessage() doesn't work here. 163 // 'console' must munge this up. 164 System.err.println(e); 165 166 if (! ISBATCH) console.interact(""); 167 // else exit(1); 168 } 169 170 // Add a shutdownHook to the JVM 171 runtime.addShutdownHook(new Thread() { 172 public void run( ) { 173 if (dbConnection != null) { 174 System.err.println("Closing database connection."); 175 try { dbConnection.close(); } 176 catch (SQLException e) { System.err.println(e); } 177 } 178 } 179 }); 180 181 if (! ISBATCH) console.interact(""); 182 // else exit(0); 183 } 184 185 //-------------------------------------------------------------------------- 186 // Getters and Setters 187 188 /** Set the DBConnection. */ 189 public static void setConnection(Connection conn) { dbConnection = conn; } 190 191 //-------------------------------------------------------------------------- 192 // Miscellaneous Methods 193 194 public static void exit(int code) { runtime.exit(code); } 195 196 public static void testProxy(String server, String database, 197 String user, String passwd, String sql) { 198 try { 199 // Construct data 200 String data = URLEncoder.encode("sid", "UTF-8") + "=" + URLEncoder.encode(database, "UTF-8"); 201 data += "&" + URLEncoder.encode("user", "UTF-8") + "=" + URLEncoder.encode(user, "UTF-8"); 202 data += "&" + URLEncoder.encode("passwd", "UTF-8") + "=" + URLEncoder.encode(passwd, "UTF-8"); 203 data += "&" + URLEncoder.encode("sql", "UTF-8") + "=" + URLEncoder.encode(sql, "UTF-8"); 204 205 System.out.println(data); 206 207 // Send data 208 URL url = new URL(server); 209 URLConnection conn = url.openConnection(); 210 conn.setDoOutput(true); 211 OutputStreamWriter wr = new OutputStreamWriter(conn.getOutputStream()); 212 wr.write(data); 213 wr.flush(); 214 215 // Get the response 216 BufferedReader rd = new BufferedReader(new InputStreamReader(conn.getInputStream())); 217 String line; 218 while ((line = rd.readLine()) != null) { 219 // Return output, one line at a time 220 CrimsonUtils.printMsg(line); 221 } 222 wr.close(); 223 rd.close(); 224 } catch (Exception e) { 225 CrimsonUtils.printError(e.getMessage()); 226 } 227 } 228 229 /** 230 * This method can be used to test encryption and checksum 231 * connection schemes. 232 */ 233 public static void testConn(String dbType, String server, String port, String database, 234 String user, String passwd) { 235 // register the dbType with SQL() so we can use the 236 // appropriate drivers 237 if (! SQL.setDBType(dbType)) { 238 CrimsonUtils.printError("Unable to set database type to: " + dbType); 239 return; 240 } 241 242 try { 243 String url = SQL.dbURL() + server; 244 if (dbType.equalsIgnoreCase("oracle")) { 245 // url = SQL.dbURL() + server + ":" + port + "/" + database; 246 if (! CrimsonUtils.isEmpty(port)) url += ":" + port; 247 if (! CrimsonUtils.isEmpty(database)) url += "/" + database; 248 } else if (dbType.equalsIgnoreCase("mysql")) { 249 // url = SQL.dbURL() + server + "/" + database; 250 if (! CrimsonUtils.isEmpty(database)) url += "/" + database; 251 } else { 252 CrimsonUtils.printError("Unknown database type (use Oracle or MySql): " + dbType); 253 return; 254 } 255 256 Connection conn = DriverManager.getConnection(url, user, passwd); 257 258 // Create a Statement 259 Statement stmt = conn.createStatement(); 260 ResultSet rset = stmt.executeQuery(SQL.getDate()); 261 262 // Iterate through the result and print them 263 while (rset.next()) System.out.println(rset.getString(1)); 264 265 conn.close(); 266 } catch (Exception e) { e.printStackTrace(); } 267 } 268 269 /** 270 * On Windows machines, exec() requires "cmd /c" to envoke the 271 * command prompt. The boolean ISDOS is defaulted to false but 272 * can be set manually or via the command line. This returns true 273 * if the command ran successfully and false if the command 274 * returned a non 0 exit code or any exceptions occurred. 275 * @XXX BufferedInputStream.available() should say if there is 276 * data in the stream but it doesn't seem to work correctly. It 277 * seems that in many conditions you must first read from the 278 * stream which is not useful. 279 * @XXX ISDOS should be a parameter. 280 * @XXX Linux used to require "sh -c" but doesn't anymore. Not sure why. 281 */ 282 public static boolean exec(String cmd) { 283 return exec(cmd, false); 284 } 285 286 public static boolean exec(String cmd, boolean printOutput) { 287 Process p; 288 289 try { 290 if (ISDOS) { 291 String[] command = {"", "", cmd}; 292 command[0] = "cmd"; 293 command[1] = "/c"; 294 p = runtime.exec(command); 295 } else { 296 /* 297 // this may not be the correct command for Mac OS 298 command[0] = "sh"; 299 command[1] = "-c"; 300 */ 301 302 p = runtime.exec(cmd); 303 } 304 305 // this doesn't work but should 306 // InputStream in = new BufferedInputStream(p.getInputStream()); 307 // System.out.println(in.available()); 308 309 BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream())); 310 String msg = null; 311 while ((msg = in.readLine()) != null) { 312 if (printOutput) CrimsonUtils.printMsg(msg); 313 } 314 315 // if the process has any errors, print them and return 316 // false on exit 317 BufferedReader err = new BufferedReader(new InputStreamReader(p.getErrorStream())); 318 msg = err.readLine(); 319 if (msg != null) { 320 CrimsonUtils.printError(msg); 321 while ((msg = err.readLine()) != null) { 322 CrimsonUtils.printError(msg); 323 } 324 return false; 325 } 326 } catch (Exception e) { 327 CrimsonUtils.printError(e.toString()); 328 return false; 329 } 330 331 return true; 332 } 333 334 /* 335 * Runs cmd returning the command output. Errors returned by the 336 * command are printed to the screen. If an error occurs running 337 * command, then null is returned. 338 */ 339 public static String getExec(String cmd) { return getExec(cmd, false); } 340 341 /* 342 * Runs cmd returning the command output. If incError is false, 343 * errors returned by the command are printed to the screen and 344 * null is returned (when an error occurs). If incError is true 345 * then errors are included in the output. This is helpful when 346 * need to grab STDERR. 347 */ 348 public static String getExec(String cmd, boolean incError) { 349 Process p; 350 String out = ""; 351 352 try { 353 if (ISDOS) { 354 String[] command = {"", "", cmd}; 355 command[0] = "cmd"; 356 command[1] = "/c"; 357 p = runtime.exec(command); 358 } else { 359 p = runtime.exec(cmd); 360 } 361 362 BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream())); 363 String msg = null; 364 while ((msg = in.readLine()) != null) { 365 out += msg + "\n"; 366 } 367 368 // if the process has any errors, print them and return 369 // false on exit 370 BufferedReader err = new BufferedReader(new InputStreamReader(p.getErrorStream())); 371 msg = err.readLine(); 372 if (msg != null) { 373 if (incError) out += msg + "\n"; 374 else CrimsonUtils.printError(msg); 375 376 while ((msg = err.readLine()) != null) { 377 if (incError) out += msg + "\n"; 378 else CrimsonUtils.printError(msg); 379 } 380 if (! incError) return null; 381 } 382 } catch (Exception e) { 383 CrimsonUtils.printError(e.toString()); 384 return null; 385 } 386 387 return out; 388 } 389 390 /** 391 * On Windows machines, exec() requires "cmd /c" to envoke the 392 * command prompt. The boolean ISDOS is defaulted to false but 393 * can be set manually or via the command line. This returns true 394 * if the command ran successfully and false if the command 395 * returned a non 0 exit code or any exceptions occurred. 396 * @XXX ISDOS should be a parameter. 397 */ 398 public static boolean execFull(String cmd, boolean wait) { 399 Process p; 400 401 // CrimsonUtils.printMsg("Executing shell command: " + cmd); 402 403 try { 404 String[] command = {"", "", cmd}; 405 if (ISDOS) { 406 command[0] = "cmd"; 407 command[1] = "/c"; 408 p = runtime.exec(command); 409 } else { 410 p = runtime.exec(cmd); 411 } 412 413 // print any output from process 414 Reader in = new InputStreamReader(p.getInputStream()); 415 int c = in.read(); 416 StringBuffer str = new StringBuffer(); 417 while (c != -1) { 418 str.append((char) c); 419 c = in.read(); 420 } 421 CrimsonUtils.printMsg(str.toString()); 422 423 if (wait) p.waitFor(); 424 425 if (p.exitValue() != 0) { 426 // print any errors from process 427 in = new InputStreamReader(p.getErrorStream()); 428 c = in.read(); 429 str = new StringBuffer(); 430 while (c != -1) { 431 str.append((char) c); 432 c = in.read(); 433 } 434 CrimsonUtils.printError(str.toString()); 435 436 return false; 437 } 438 } catch (Exception e) { 439 CrimsonUtils.printError(e.toString()); 440 return false; 441 } 442 443 return true; 444 } 445 446 public static String getSystemProperty(String property) { 447 return System.getProperty(property); 448 } 449 450 public static void setSystemProperty(String property, String value) { 451 if ((property == null) || (property == "")) return; 452 System.setProperty(property, value); 453 } 454 455 //-------------------------------------------------------------------------- 456 // Memory Related Methods 457 458 /** 459 * A simple class to experiment with your JVM's garbage collector 460 * and memory sizes for various data types. 461 * 462 * @author <a href="mailto:vlad@trilogy.com">Vladimir Roubtsov</a> 463 */ 464 public static void sizeOf() { 465 // "warm up" all classes/methods that we are going to use: 466 runGC(); 467 usedMemory(); 468 469 // array to keep strong references to allocated objects: 470 final int count = 10000; // 10000 or so is enough for small ojects 471 Object[] objects = new Object[count]; 472 473 long heap1 = 0; 474 475 // allocate count+1 objects, discard the first one: 476 for (int i = -1; i < count; ++i) { 477 Object object = null; 478 479 // INSTANTIATE YOUR DATA HERE AND ASSIGN IT TO 'object': 480 481 /* 482 try { 483 object = obj.getClass().newInstance(); 484 } catch (Exception e) { ; } 485 */ 486 object = new Species("", null); // 128 bytes 487 //object = new Object(); // 8 bytes 488 //object = new Integer(i); // 16 bytes 489 //object = new Long(i); // 16 bytes 490 //object = new Double(i); // 16 bytes 491 //object = new TreeSet(); // 72 bytes 492 //object = new String(""); // 24 bytes 493 //object = new char[10]; // 32 bytes 494 //object = new byte[32][1]; // 656 bytes?! 495 496 if (i >= 0) 497 objects[i] = object; 498 else { 499 object = null; // discard the "warmup" object 500 runGC(); 501 heap1 = usedMemory(); // take a "before" heap snapshot 502 } 503 } 504 505 runGC(); 506 long heap2 = usedMemory(); // take an "after" heap snapshot: 507 508 final int size = Math.round (((float)(heap2 - heap1))/count); 509 System.out.println ("'before' heap: " + heap1 + ", 'after' heap: " + heap2); 510 System.out.println ("heap delta: " + (heap2 - heap1) + 511 ", {" + objects [0].getClass () + "} size = " + size + " bytes"); 512 } 513 514 // a helper method for creating Strings of desired length 515 // and avoiding getting tricked by String interning: 516 public static String createString(final int length) { 517 final char[] result = new char[length]; 518 for (int i = 0; i < length; ++i) result[i] = (char) i; 519 520 return new String (result); 521 } 522 523 // this is our way of requesting garbage collection to be run: 524 // [how aggressive it is depends on the JVM to a large degree, but 525 // it is almost always better than a single Runtime.gc() call] 526 public static void runGC() { 527 // for whatever reason it helps to call Runtime.gc() 528 // using several method calls: 529 try { 530 for (int r = 0; r < 4; ++r) _runGC(); 531 } catch (Exception e) { CrimsonUtils.printError(e.getMessage()); } 532 } 533 534 public static void _runGC() { 535 long usedMem1 = usedMemory(), usedMem2 = Long.MAX_VALUE; 536 537 try { 538 for (int i = 0; (usedMem1 < usedMem2) && (i < 1000); ++i) { 539 runtime.runFinalization(); 540 runtime.gc(); 541 Thread.currentThread().yield(); 542 543 usedMem2 = usedMem1; 544 usedMem1 = usedMemory(); 545 } 546 } catch (Exception e) { CrimsonUtils.printError(e.getMessage()); } 547 } 548 549 public static long usedMemory() { 550 return runtime.totalMemory() - runtime.freeMemory(); 551 } 552 553 /** Make sure the database connection is closed on exit. */ 554 /* 555 protected void finalize() throws Throwable { 556 if (dbConnection != null) { 557 System.err.println("Closing database connection."); 558 try { dbConnection.close(); } 559 catch (SQLException e) { 560 System.err.println(e.getMessage()); 561 } 562 } 563 super.finalize(); 564 } 565 */ 566 } // CrimsonMain.java