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