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.util.newjvm;
038:
039: import java.io.*; //import java.net.InetAddress;
040: //import java.net.InetSocketAddress;
041: import java.rmi.server.UnicastRemoteObject;
042: import java.rmi.RemoteException;
043:
044: import java.util.Arrays;
045:
046: import edu.rice.cs.util.Log;
047: import edu.rice.cs.util.StringOps;
048: import edu.rice.cs.util.UnexpectedException;
049: import edu.rice.cs.util.swing.ScrollableDialog;
050: import edu.rice.cs.util.swing.Utilities;
051:
052: import static edu.rice.cs.drjava.config.OptionConstants.*;
053:
054: /** This class is the root class for the Slave JVM. The Master JVM invokes the {@link #main} method of this class,
055: * which is never instantiated. See the {@link #main} method documentation for information on the command line
056: * parameters this class requires. If there is an error setting up the slave JVM before the RMI links can be
057: * established, this JVM process will exit with an error code according to the following list:
058: * <DL>
059: * <DT>1</DT><DD>Invalid number of command line arguments.</DD>
060: * <DT>2</DT><DD>Error deserializing remote stub</DD>
061: * <DT>3</DT><DD>Error instantiating slave implementation class</DD>
062: * </DL>
063: * If the slave JVM completes successfully, it will exit with code 0.
064: *
065: * @version $Id: SlaveJVMRunner.java 4260 2007-10-10 20:28:34Z mgricken $
066: */
067: public final class SlaveJVMRunner {
068:
069: /** Whether Swing dialogs should be displayed with diagnostic
070: * information if the slave is unable to register or contact the
071: * main JVM. If false, the information will be printed to (the
072: * usually invisible) System.err.
073: *
074: * Note that the master JVM will always be notified if possible if
075: * there is a problem instantiating or registering the slave, so
076: * it can take appropriate action. This flag only affects those
077: * situations in which the master JVM cannot be contacted.
078: */
079: public static final boolean SHOW_DEBUG_DIALOGS = false;
080:
081: protected static final Log _log = new Log("MasterSlave.txt", false);
082:
083: private static final long RMI_TIMEOUT = 5000L;
084:
085: private static Thread _main;
086:
087: private static volatile boolean _notDone;
088:
089: /** Private constructor to prevent instantiation. */
090: private SlaveJVMRunner() {
091: }
092:
093: private static AbstractSlaveJVM _getInstance(Class clazz)
094: throws Exception {
095: try {
096: return (AbstractSlaveJVM) clazz.getField("ONLY").get(null);
097: } catch (Throwable t) {
098: _log.log("SlaveRemote class does not have an ONLY field!");
099: return (AbstractSlaveJVM) clazz.newInstance();
100: }
101: }
102:
103: /** The main method for invoking a slave JVM.
104: *
105: * @param args Command-line parameters, of which there must be two or three. The first is the absolute path to the
106: * file containing the serialized MasterRemote stub, and the second is the fully-qualified class name of the
107: * slave JVM implementation class.
108: */
109: public synchronized static void main(String[] args) {
110: try {
111: // Make sure RMI doesn't use an IP address that might change
112: System.setProperty("java.rmi.server.hostname", "127.0.0.1");
113:
114: assert (args.length == 3) || (args.length == 2);
115:
116: _notDone = true;
117:
118: _main = Thread.currentThread();
119:
120: // get the master remote
121: final FileInputStream fstream = new FileInputStream(args[0]);
122: final ObjectInputStream ostream = new ObjectInputStream(
123: new BufferedInputStream(fstream));
124:
125: _log.log("Slave JVM reading master remote stub from file "
126: + args[0] + " with " + fstream.getChannel().size()
127: + " bytes");
128:
129: /* The following code currently breaks unit tests and DrJava itself when it detects the hanging
130: * of readObject(...). It can be commented back if the calling code is revised to handle this form
131: * of exit. */
132:
133: // Thread timeout = new Thread("RMI Timeout Thread") {
134: // public void run() {
135: // _log.log("RMI timer started");
136: //
137: // final Object lock = new Object();
138: // try { synchronized(lock) { lock.wait(RMI_TIMEOUT); } }
139: // catch(InterruptedException e) { throw new UnexpectedException(e); }
140: // // Abort starting this slave JVM if readObject has hung
141: // if (_notDone) {
142: // StackTraceElement[] trace = Thread.getAllStackTraces().get(_main);
143: // _log.log("DUMP of hung deserializing thread:", trace);
144: // System.exit(9);
145: // }
146: // else _log.log(this + " TERMINATED normally");
147: // }
148: // };
149: //
150: // timeout.setDaemon(true);
151: // timeout.start();
152:
153: // // Loading the class that intermittently hangs first readObject(...) call below
154: // Class psi = Class.forName("java.net.PlainSocketImpl");
155: final MasterRemote masterRemote = (MasterRemote) ostream
156: .readObject();
157: _notDone = false;
158: _log
159: .log("SlaveJVMRunner completed reading "
160: + masterRemote);
161: fstream.close();
162: ostream.close();
163:
164: AbstractSlaveJVM slave = null;
165:
166: try {
167: Class slaveClass = Class.forName(args[1]);
168: // _log.log("Slave JVM created singleton of " + args[1]);
169: slave = _getInstance(slaveClass);
170:
171: //Export slave object to RMI, passing stub to the master JVM (how does stub get there? Transitivity?
172: // _log.log("Slave JVM exporting " + slave + " to RMI");
173: SlaveRemote slaveRemote = (SlaveRemote) UnicastRemoteObject
174: .toStub(slave);
175: _log.log("SlaveJVMRunner exported stub " + slaveRemote);
176:
177: // start the slave and then notify the master
178: // _log.log("Slave JVM invoking the method " + slave + ".start(" + masterRemote + ")");
179: slave.start(masterRemote);
180: _log
181: .log("SlaveJVMRunner invoking the method registerSlave("
182: + slave + ") in the Master JVM");
183: masterRemote.registerSlave(slave);
184: } catch (Exception e) {
185: // Couldn't instantiate the slave.
186: _log
187: .log("SlaveJVMRunner could not instantiate and start slave class '"
188: + slave + "'. Threw exception: " + e);
189: try {
190: // Try to show the error properly, through the master
191: masterRemote.errorStartingSlave(e);
192: } catch (RemoteException re) {
193: // Couldn't show the error properly, so use another approach
194: String msg = "SlaveJVMRunner could not instantiate and register the slave.\n"
195: + " Also failed to display error through master JVM, because:\n"
196: + StringOps.getStackTrace(re) + "\n";
197: _showErrorMessage(msg, e);
198: }
199: System.exit(3);
200: }
201: } catch (Exception e) { // IOException, ClassNotFoundException
202: // There's no master to display the error, so we'll do it ourselves
203: _showErrorMessage(
204: "SlaveJVMRunner could not set up the Slave JVM.", e);
205: _log
206: .log("SlaveJVMRunner could not set up the Slave JVM. Calling System.exit(2) in response to: "
207: + e);
208: System.exit(2);
209: }
210: }
211:
212: /** Displays a graphical error message to notify the user of a problem encountered while starting the slave JVM.
213: * @param cause A message indicating where the error took place.
214: * @param t The Throwable which caused the error.
215: */
216: private static void _showErrorMessage(String cause, Throwable t) {
217: String msg = "An error occurred in SlaveJVMRunner while starting the slave JVM:\n "
218: + cause
219: + "\n\nOriginal error:\n"
220: + StringOps.getStackTrace(t);
221:
222: _log.log("ERROR in SlaveJVMRunner: " + cause + "; threw " + t);
223:
224: if (SHOW_DEBUG_DIALOGS)
225: new ScrollableDialog(null, "Error", "Error details:", msg)
226: .show();
227: else if (!Utilities.TEST_MODE)
228: System.out.println(msg);
229: }
230: }
|