001: /*BEGIN_COPYRIGHT_BLOCK
002: *
003: * Copyright (c) 2001-2007, JavaPLT group at Rice University (javaplt@rice.edu)
004: * All rights reserved.
005: *
006: * Redistribution and use in source and binary forms, with or without
007: * modification, are permitted provided that the following conditions are met:
008: * * Redistributions of source code must retain the above copyright
009: * notice, this list of conditions and the following disclaimer.
010: * * Redistributions in binary form must reproduce the above copyright
011: * notice, this list of conditions and the following disclaimer in the
012: * documentation and/or other materials provided with the distribution.
013: * * Neither the names of DrJava, the JavaPLT group, Rice University, nor the
014: * names of its contributors may be used to endorse or promote products
015: * derived from this software without specific prior written permission.
016: *
017: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
018: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
019: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
020: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
021: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
022: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
023: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
024: * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
025: * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
026: * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
027: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
028: *
029: * This software is Open Source Initiative approved Open Source Software.
030: * Open Source Initative Approved is a trademark of the Open Source Initiative.
031: *
032: * This file is part of DrJava. Download the current version of this project
033: * from http://www.drjava.org/ or http://sourceforge.net/projects/drjava/
034: *
035: * END_COPYRIGHT_BLOCK*/
036:
037: package edu.rice.cs.drjava;
038:
039: import static edu.rice.cs.drjava.config.OptionConstants.*;
040:
041: import java.io.File;
042: import java.io.IOException;
043: import java.io.ByteArrayOutputStream;
044: import java.net.MalformedURLException;
045: import java.net.URL;
046: import java.net.URLClassLoader;
047: import java.util.ArrayList;
048: import java.util.LinkedList;
049: import java.util.List;
050: import java.util.jar.JarFile;
051: import java.util.jar.Manifest;
052:
053: import javax.swing.JFileChooser;
054: import javax.swing.JOptionPane;
055: import javax.swing.SwingUtilities;
056:
057: import edu.rice.cs.drjava.config.FileConfiguration;
058: import edu.rice.cs.drjava.config.FileOption;
059: import edu.rice.cs.drjava.platform.PlatformFactory;
060: import edu.rice.cs.drjava.ui.DrJavaErrorHandler;
061: import edu.rice.cs.drjava.ui.ClassPathFilter;
062: import edu.rice.cs.drjava.ui.SplashScreen;
063: import edu.rice.cs.util.ArgumentTokenizer;
064: import edu.rice.cs.util.FileOps;
065: import edu.rice.cs.util.Log;
066: import edu.rice.cs.util.newjvm.ExecJVM;
067: import edu.rice.cs.plt.debug.DebugUtil;
068:
069: /** Startup class for DrJava consisting entirely of static members. The main method reads the .drjava file (creating
070: * one if none exists) to get the critical information required to start the main JVM for DrJava:
071: * (i) the location of tools.jar in the Java JDK installed on this machine (so DrJava can invoke the javac compiler
072: * stored in tools.jar)
073: * (ii) the argument string for invoking the main JVM (notably -X options used to determine maximum heap size, etc.)
074: * @version $Id: DrJava.java 4267 2007-11-26 23:02:36Z mgricken $
075: */
076: public class DrJava {
077:
078: private static Log _log = new Log("DrJava.txt", false);
079:
080: private static final String DEFAULT_MAX_HEAP_SIZE_ARG = "-Xmx128M";
081:
082: private static final ArrayList<String> _filesToOpen = new ArrayList<String>();
083: private static final ArrayList<String> _jvmArgs = new ArrayList<String>();
084:
085: static volatile boolean _showDebugConsole = false;
086:
087: /** true if a new instance of DrJava should be started instead of
088: * connecting to an already running instance. */
089: static volatile boolean _forceNewInstance = false;
090:
091: /* Config objects can't be public static final, since we have to delay construction until we know the
092: * config file's location. (Might be specified on command line.) Instead, use accessor methods to
093: * prevent others from assigning new values. */
094:
095: /** Default properties file used by the configuration object, i.e. ".drjava" in the user's home directory. */
096: public static final File DEFAULT_PROPERTIES_FILE = new File(System
097: .getProperty("user.home"), ".drjava");
098:
099: /** Properties file used by the configuration object. Defaults to DEFAULT_PROPERTIES_FILE. */
100: private static volatile File _propertiesFile = DEFAULT_PROPERTIES_FILE;
101:
102: /** Configuration object with all customized and default values. Initialized from _propertiesFile. */
103: private static volatile FileConfiguration _config = _initConfig();
104:
105: /** Returns the properties file used by the configuration object. */
106: public static File getPropertiesFile() {
107: return _propertiesFile;
108: }
109:
110: /** Returns the configuration object with all customized and default values. */
111: public static FileConfiguration getConfig() {
112: return _config;
113: }
114:
115: /** @return an array of the files that were passed on the command line. */
116: public static String[] getFilesToOpen() {
117: return _filesToOpen.toArray(new String[0]);
118: }
119:
120: /** @return true if the debug console should be enabled */
121: public static boolean getShowDebugConsole() {
122: return _showDebugConsole;
123: }
124:
125: /** Starts running DrJava.
126: * @param args Command line argument array
127: */
128: public static void main(final String[] args) {
129: // Platform-specific UI setup.
130: PlatformFactory.ONLY.beforeUISetup();
131:
132: // handleCommandLineArgs will return true if DrJava should be loaded
133: if (handleCommandLineArgs(args)) {
134: if (!_forceNewInstance
135: && DrJava
136: .getConfig()
137: .getSetting(
138: edu.rice.cs.drjava.config.OptionConstants.REMOTE_CONTROL_ENABLED)
139: && (_filesToOpen.size() > 0)) {
140: try {
141: boolean ret = RemoteControlClient.openFile(null);
142: if (!RemoteControlClient.isServerRunning()) {
143: // server not running, display splash screen
144: new SplashScreen().flash();
145: }
146: } catch (IOException ioe) {
147: // ignore
148: }
149: } else {
150: // either forcing new instance or no files specified, display splash screen
151: new SplashScreen().flash();
152: }
153:
154: // Utilities.showDebug("Calling configureAndLoadDrJavaRoot with args = " + args);
155: configureAndLoadDrJavaRoot(args);
156: }
157: }
158:
159: public static void configureAndLoadDrJavaRoot(String[] args) {
160: try {
161: // if there were files passed on the command line,
162: // try to open them in an existing instance
163: if (!_forceNewInstance
164: && DrJava
165: .getConfig()
166: .getSetting(
167: edu.rice.cs.drjava.config.OptionConstants.REMOTE_CONTROL_ENABLED)
168: && (_filesToOpen.size() > 0)) {
169: try {
170: boolean ret = RemoteControlClient.openFile(null);
171: if (RemoteControlClient.isServerRunning()) {
172: // existing instance is running and responding
173: for (int i = 0; i < _filesToOpen.size(); ++i) {
174: RemoteControlClient.openFile(new File(
175: _filesToOpen.get(i)));
176: }
177: // files opened in existing instance, quit
178: System.exit(0);
179: }
180: } catch (IOException ioe) {
181: ioe.printStackTrace();
182: }
183: }
184:
185: // Restart if there are custom JVM args
186: boolean restart = getConfig().getSetting(MASTER_JVM_ARGS)
187: .length() > 0;
188:
189: LinkedList<String> classArgsList = new LinkedList<String>();
190: classArgsList.addAll(_filesToOpen);
191:
192: // Add the parameters "-debugConsole" to classArgsList if _showDebugConsole is true
193: if (_showDebugConsole) {
194: classArgsList.addFirst("-debugConsole");
195: }
196:
197: if (!_propertiesFile.equals(DEFAULT_PROPERTIES_FILE)) {
198: // Placed in reversed order to get "-config filename"
199: classArgsList.addFirst(_propertiesFile
200: .getAbsolutePath());
201: classArgsList.addFirst("-config");
202: }
203:
204: String[] classArgs = classArgsList.toArray(new String[0]);
205:
206: if (restart) {
207: String classPath = System
208: .getProperty("java.class.path");
209:
210: // Run a new copy of DrJava and exit
211: try {
212: // Utilities.showDebug("Starting DrJavaRoot with classArgs = " + Arrays.toString(classArgs) + "; classPath = " + classPath +
213: // "; jvmArgs = " + _jvmArgs + "; workDir = " + workDir);
214: ExecJVM.runJVM("edu.rice.cs.drjava.DrJavaRoot",
215: classArgs, classPath, _jvmArgs
216: .toArray(new String[0]), null);
217: } catch (IOException ioe) {
218: // Display error
219: final String[] text = {
220: "DrJava was unable to load its compiler and debugger. Would you ",
221: "like to start DrJava without a compiler and debugger?",
222: "\nReason: " + ioe.toString() };
223: int result = JOptionPane.showConfirmDialog(null,
224: text,
225: "Could Not Load Compiler and Debugger",
226: JOptionPane.YES_NO_OPTION);
227: if (result != JOptionPane.YES_OPTION) {
228: System.exit(0);
229: }
230: }
231: }
232:
233: else {
234: // No restart -- just invoke DrJavaRoot.main.
235: DrJavaRoot.main(classArgs);
236: }
237: } catch (Throwable t) {
238: // Show any errors to the System.err and in an DrJavaErrorHandler
239: System.out.println(t.getClass().getName() + ": "
240: + t.getMessage());
241: t.printStackTrace(System.err);
242: System.out.println("error thrown");
243: new DrJavaErrorHandler().handle(t);
244: }
245: }
246:
247: /** Handles any command line arguments that have been specified.
248: * @return true if DrJava should load, false if not
249: */
250: static boolean handleCommandLineArgs(String[] args) {
251: boolean heapSizeGiven = false; // indicates whether args includes an argument of the form -Xmx<number>
252:
253: // Loop through arguments looking for known options
254: int argIndex = 0;
255: int len = args.length;
256: _filesToOpen.clear();
257:
258: while (argIndex < len) {
259: String arg = args[argIndex++];
260:
261: if (arg.equals("-config")) {
262: if (len == argIndex) {
263: // config option is missing file name; should we generate an error?
264: return true;
265: }
266: // arg.length > i+1 implying args list incudes config file name and perhaps files to open
267: setPropertiesFile(args[argIndex++]);
268: _config = _initConfig(); // read specified .djrava file into _config
269: }
270:
271: else if (arg.startsWith("-X") || arg.startsWith("-D")) {
272: if (arg.startsWith("-Xmx")) {
273: heapSizeGiven = true;
274: }
275: _jvmArgs.add(arg);
276: }
277:
278: else if (arg.equals("-debugConsole"))
279: _showDebugConsole = true;
280:
281: else if (arg.equals("-new"))
282: _forceNewInstance = true;
283:
284: else if (arg.equals("-help") || arg.equals("-?")) {
285: displayUsage();
286: return false;
287: } else {
288: // this is the first file to open, do not consume
289: --argIndex;
290: break;
291: }
292: }
293:
294: List<String> configArgs = ArgumentTokenizer
295: .tokenize(getConfig().getSetting(MASTER_JVM_ARGS));
296: for (String arg : configArgs) {
297: if (arg.startsWith("-Xmx")) {
298: heapSizeGiven = true;
299: }
300: _jvmArgs.add(arg);
301: }
302:
303: if (PlatformFactory.ONLY.isMacPlatform()) {
304: String iconLoc = System
305: .getProperty("edu.rice.cs.drjava.icon");
306: if (iconLoc != null) { // we are running inside the Mac app wrapper
307: _jvmArgs.add("-Xdock:name=DrJava");
308: _jvmArgs.add("-Xdock:icon=" + iconLoc);
309: }
310: }
311:
312: if (!heapSizeGiven) {
313: _jvmArgs.add(DEFAULT_MAX_HEAP_SIZE_ARG);
314: }
315:
316: _log.log("_jvmArgs = " + _jvmArgs);
317:
318: // Open the remaining args as filenames
319:
320: for (int i = argIndex; i < len; i++) {
321: _filesToOpen.add(args[i]);
322: }
323: return true;
324: }
325:
326: /** Displays a usage message about the available options. */
327: static void displayUsage() {
328: final StringBuilder buf = new StringBuilder();
329: buf.append("Usage: java -jar drjava.jar [OPTIONS] [FILES]\n\n");
330: buf.append("where options include:\n");
331: buf
332: .append(" -config [FILE] use a custom config file\n");
333: buf
334: .append(" -new force the creation of a new DrJava instance;");
335: buf
336: .append(" do not connect to existing instance");
337: buf.append(" -help | -? print this help message\n");
338: buf
339: .append(" -X<jvmOption> specify a JVM configuration option for the master DrJava JVM\n");
340: buf
341: .append(" -D<name>[=<value>] set a Java property for the master DrJava JVM\n");
342: System.out.print(buf.toString());
343: }
344:
345: // /** Prompts the user that the location of tools.jar needs to be specified to be able to use the compiler and/or the
346: // * debugger.
347: // * @param needCompiler whether DrJava needs tools.jar for a compiler
348: // * @param needDebugger whether DrJava needs tools.jar for the debugger
349: // */
350: // public static void promptForToolsJar(boolean needCompiler, boolean needDebugger) {
351: // File selectedFile = getConfig().getSetting(JAVAC_LOCATION);
352: // String selectedVersion = _getToolsJarVersion(selectedFile);
353: //
354: // final String[] text;
355: // if (selectedVersion==null) {
356: // text = new String[] {
357: // "DrJava cannot find a 'tools.jar' file for the version of Java ",
358: // "that is being used to run DrJava (Java version "+System.getProperty("java.version")+").",
359: // "Would you like to specify the location of the requisite 'tools.jar' file?",
360: // "If you say 'No', DrJava might be unable to compile or debug Java programs."
361: // };
362: // }
363: // else {
364: // text = new String[] {
365: // "DrJava cannot find a 'tools.jar' file for the version of Java ",
366: // "that is being used to run DrJava (Java version "+System.getProperty("java.version")+").",
367: // "The file you have selected appears to be for version "+selectedVersion+".",
368: // "Would you like to specify the location of the requisite 'tools.jar' file?",
369: // "If you say 'No', DrJava might be unable to compile or debug Java programs.)"
370: // };
371: // }
372: //
373: // int result = JOptionPane.showConfirmDialog(null, text, "Locate 'tools.jar'?", JOptionPane.YES_NO_OPTION);
374: //
375: // if (result == JOptionPane.YES_OPTION) {
376: // JFileChooser chooser = new JFileChooser();
377: // chooser.setFileFilter(new ClassPathFilter() {
378: // public boolean accept(File f) {
379: // if (f.isDirectory()) return true;
380: // String ext = getExtension(f);
381: // return ext != null && ext.equals("jar");
382: // }
383: // public String getDescription() { return "Jar Files"; }
384: // });
385: //
386: // // Loop until we find a good tools.jar or the user gives up
387: // do {
388: // if (chooser.showOpenDialog(null) == JFileChooser.APPROVE_OPTION) {
389: // File jar = chooser.getSelectedFile();
390: //
391: // if (jar != null) {
392: // // set the tools.jar property
393: // getConfig().setSetting(JAVAC_LOCATION, jar);
394: //
395: // // Adjust if we needed a compiler
396: // if (needCompiler && classLoadersCanFind(TEST_COMPILER_CLASS)) needCompiler = false;
397: //
398: // // Adjust if we need a debugger
399: // if (needDebugger && classLoadersCanFind(TEST_DEBUGGER_CLASS)) needDebugger = false;
400: // }
401: // }
402: //// Utilities.showDebug("need Compiler = " + needCompiler + "; needDebugger = " + needDebugger);
403: // }
404: // while ((needCompiler || needDebugger) && _userWantsToPickAgain());
405: //
406: // // Save config with good tools.jar if available
407: // if ((! needCompiler) && (! needDebugger)) _saveConfig();
408: // }
409: // }
410:
411: /** Switches the config object to use a custom config file. Ensures that Java source files aren't
412: * accidentally used.
413: */
414: static void setPropertiesFile(String fileName) {
415: if (!fileName.endsWith(".java"))
416: _propertiesFile = new File(fileName);
417: }
418:
419: /** Initializes the configuration object with the current notion of the properties file.
420: * @throws IllegalStateException if config has already been assigned
421: */
422: static FileConfiguration _initConfig() throws IllegalStateException {
423: // // Make sure someone doesn't try to change the config object.
424: // if (_config != null) throw new IllegalStateException("Can only call initConfig once!");
425:
426: FileConfiguration config;
427:
428: try {
429: _propertiesFile.createNewFile();
430: } // be nice and ensure a config file if there isn't one
431: catch (IOException e) { /* IOException occurred, continue without a real file */
432: }
433:
434: config = new FileConfiguration(_propertiesFile);
435: try {
436: config.loadConfiguration();
437: } catch (Exception e) {
438: // Problem parsing the config file. Use defaults and remember what happened (for the UI).
439: config.resetToDefaults();
440: config.storeStartupException(e);
441: }
442: _config = config; // required to support calls on DrJava._initConfig() in unit tests
443: return config;
444: }
445:
446: /** Saves the contents of the config file. TO DO: log any IOExceptions that occur. */
447: protected static void _saveConfig() {
448: try {
449: getConfig().saveConfiguration();
450: } catch (IOException e) {
451: JOptionPane
452: .showMessageDialog(
453: null,
454: "Could not save the location of tools.jar in \n"
455: + "the '.drjava' file in your home directory. \n"
456: + "Another process may be using the file.\n\n"
457: + e, "Could Not Save Changes",
458: JOptionPane.ERROR_MESSAGE);
459: // TODO: log this error
460: }
461: }
462:
463: // /** Displays a prompt to the user indicating that tools.jar could not be found in the specified location, and asks
464: // * if he would like to specify a new location.
465: // */
466: // private static boolean _userWantsToPickAgain() {
467: // File selectedFile = getConfig().getSetting(JAVAC_LOCATION);
468: // String selectedVersion = _getToolsJarVersion(selectedFile);
469: //
470: // final String[] text;
471: // if (selectedVersion==null) {
472: // text = new String[] {
473: // "The file you chose did not appear to be the correct 'tools.jar'",
474: // "that is compatible with the version of Java that is used to",
475: // "run DrJava (Java version "+System.getProperty("java.version")+").",
476: // "Your choice might be an incompatible version of the file.",
477: // "Would you like to pick again? The 'tools.jar' file is ",
478: // "generally located in the 'lib' subdirectory under your ",
479: // "JDK installation directory.",
480: // "(If you say 'No', DrJava might be unable to compile or ",
481: // "debug programs.)"
482: // };
483: // }
484: // else {
485: // text = new String[] {
486: // "The file you chose did not appear to be the correct 'tools.jar'",
487: // "that is compatible with the version of Java that is used to",
488: // "run DrJava (Java version "+System.getProperty("java.version")+").",
489: // "The file you have selected appears to be for",
490: // "Java version "+selectedVersion+".",
491: // "Your choice might be an incompatible version of the file.",
492: // "Would you like to pick again? The 'tools.jar' file is ",
493: // "generally located in the 'lib' subdirectory under your ",
494: // "JDK installation directory.",
495: // "If you say 'No', DrJava might be unable to compile or ",
496: // "debug programs."
497: // };
498: // }
499: //
500: // int result = JOptionPane.showConfirmDialog(null, text, "Locate 'tools.jar'?", JOptionPane.YES_NO_OPTION);
501: // return result == JOptionPane.YES_OPTION;
502: // }
503: }
|