001: package org.drools.eclipse.launching;
002:
003: import java.io.File;
004: import java.io.IOException;
005: import java.io.InterruptedIOException;
006: import java.util.ArrayList;
007: import java.util.List;
008: import java.util.Map;
009:
010: import org.drools.base.mvel.MVELDebugHandler;
011: import org.drools.eclipse.debug.core.DroolsDebugModel;
012: import org.eclipse.core.runtime.CoreException;
013: import org.eclipse.core.runtime.IProgressMonitor;
014: import org.eclipse.core.runtime.IStatus;
015: import org.eclipse.core.runtime.NullProgressMonitor;
016: import org.eclipse.core.runtime.Status;
017: import org.eclipse.core.runtime.SubProgressMonitor;
018: import org.eclipse.debug.core.DebugPlugin;
019: import org.eclipse.debug.core.ILaunch;
020: import org.eclipse.debug.core.IStatusHandler;
021: import org.eclipse.debug.core.model.IProcess;
022: import org.eclipse.debug.core.model.IStreamsProxy;
023: import org.eclipse.jdi.Bootstrap;
024: import org.eclipse.jdt.internal.launching.LaunchingMessages;
025: import org.eclipse.jdt.internal.launching.LaunchingPlugin;
026: import org.eclipse.jdt.internal.launching.LibraryInfo;
027: import org.eclipse.jdt.internal.launching.StandardVMDebugger;
028: import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants;
029: import org.eclipse.jdt.launching.IVMInstall;
030: import org.eclipse.jdt.launching.JavaRuntime;
031: import org.eclipse.jdt.launching.SocketUtil;
032: import org.eclipse.jdt.launching.VMRunnerConfiguration;
033:
034: import com.sun.jdi.VirtualMachine;
035: import com.sun.jdi.connect.Connector;
036: import com.sun.jdi.connect.IllegalConnectorArgumentsException;
037: import com.sun.jdi.connect.ListeningConnector;
038:
039: public class DroolsVMDebugger extends StandardVMDebugger {
040:
041: class ConnectRunnable implements Runnable {
042:
043: private VirtualMachine fVirtualMachine = null;
044: private ListeningConnector fConnector = null;
045: private Map fConnectionMap = null;
046: private Exception fException = null;
047:
048: public ConnectRunnable(ListeningConnector connector, Map map) {
049: fConnector = connector;
050: fConnectionMap = map;
051: }
052:
053: public void run() {
054: try {
055: fVirtualMachine = fConnector.accept(fConnectionMap);
056: } catch (IOException e) {
057: fException = e;
058: } catch (IllegalConnectorArgumentsException e) {
059: fException = e;
060: }
061: }
062:
063: public VirtualMachine getVirtualMachine() {
064: return fVirtualMachine;
065: }
066:
067: public Exception getException() {
068: return fException;
069: }
070: }
071:
072: public DroolsVMDebugger(IVMInstall vmInstance) {
073: super (vmInstance);
074: }
075:
076: public void run(VMRunnerConfiguration config, ILaunch launch,
077: IProgressMonitor monitor) throws CoreException {
078:
079: if (monitor == null) {
080: monitor = new NullProgressMonitor();
081: }
082:
083: IProgressMonitor subMonitor = new SubProgressMonitor(monitor, 1);
084: subMonitor.beginTask(
085: LaunchingMessages.StandardVMDebugger_Launching_VM____1,
086: 4);
087: subMonitor
088: .subTask(LaunchingMessages.StandardVMDebugger_Finding_free_socket____2);
089:
090: int port = SocketUtil.findFreePort();
091: if (port == -1) {
092: abort(
093: LaunchingMessages.StandardVMDebugger_Could_not_find_a_free_socket_for_the_debugger_1,
094: null,
095: IJavaLaunchConfigurationConstants.ERR_NO_SOCKET_AVAILABLE);
096: }
097:
098: subMonitor.worked(1);
099:
100: // check for cancellation
101: if (monitor.isCanceled()) {
102: return;
103: }
104:
105: subMonitor
106: .subTask(LaunchingMessages.StandardVMDebugger_Constructing_command_line____3);
107:
108: String program = constructProgramString(config);
109:
110: List arguments = new ArrayList(12);
111:
112: arguments.add(program);
113:
114: // VM args are the first thing after the java program so that users can specify
115: // options like '-client' & '-server' which are required to be the first options
116: String[] allVMArgs = combineVmArgs(config, fVMInstance);
117: addArguments(allVMArgs, arguments);
118: arguments.add("-D" + MVELDebugHandler.DEBUG_LAUNCH_KEY
119: + "=true");
120:
121: addBootClassPathArguments(arguments, config);
122:
123: String[] cp = config.getClassPath();
124: if (cp.length > 0) {
125: arguments.add("-classpath"); //$NON-NLS-1$
126: arguments.add(convertClassPath(cp));
127: }
128: double version = getJavaVersion();
129: if (version < 1.5) {
130: arguments.add("-Xdebug"); //$NON-NLS-1$
131: arguments.add("-Xnoagent"); //$NON-NLS-1$
132: }
133:
134: //check if java 1.4 or greater
135: if (version < 1.4) {
136: arguments.add("-Djava.compiler=NONE"); //$NON-NLS-1$
137: }
138: if (version < 1.5) {
139: arguments
140: .add("-Xrunjdwp:transport=dt_socket,suspend=y,address=localhost:" + port); //$NON-NLS-1$
141: } else {
142: arguments
143: .add("-agentlib:jdwp=transport=dt_socket,suspend=y,address=localhost:" + port); //$NON-NLS-1$
144: }
145:
146: arguments.add(config.getClassToLaunch());
147: addArguments(config.getProgramArguments(), arguments);
148:
149: String[] cmdLine = new String[arguments.size()];
150: arguments.toArray(cmdLine);
151:
152: String[] envp = config.getEnvironment();
153:
154: // check for cancellation
155: if (monitor.isCanceled()) {
156: return;
157: }
158:
159: subMonitor.worked(1);
160: subMonitor
161: .subTask(LaunchingMessages.StandardVMDebugger_Starting_virtual_machine____4);
162:
163: ListeningConnector connector = getConnector();
164: if (connector == null) {
165: abort(
166: LaunchingMessages.StandardVMDebugger_Couldn__t_find_an_appropriate_debug_connector_2,
167: null,
168: IJavaLaunchConfigurationConstants.ERR_CONNECTOR_NOT_AVAILABLE);
169: }
170: Map map = connector.defaultArguments();
171:
172: specifyArguments(map, port);
173: Process p = null;
174: try {
175: try {
176: // check for cancellation
177: if (monitor.isCanceled()) {
178: return;
179: }
180:
181: connector.startListening(map);
182:
183: File workingDir = getWorkingDir(config);
184: p = exec(cmdLine, workingDir, envp);
185: if (p == null) {
186: return;
187: }
188:
189: // check for cancellation
190: if (monitor.isCanceled()) {
191: p.destroy();
192: return;
193: }
194:
195: IProcess process = newProcess(launch, p,
196: renderProcessLabel(cmdLine),
197: getDefaultProcessMap());
198: process.setAttribute(IProcess.ATTR_CMDLINE,
199: renderCommandLine(cmdLine));
200: subMonitor.worked(1);
201: subMonitor
202: .subTask(LaunchingMessages.StandardVMDebugger_Establishing_debug_connection____5);
203: boolean retry = false;
204: do {
205: try {
206:
207: ConnectRunnable runnable = new ConnectRunnable(
208: connector, map);
209: Thread connectThread = new Thread(runnable,
210: "Listening Connector"); //$NON-NLS-1$
211: connectThread.setDaemon(true);
212: connectThread.start();
213: while (connectThread.isAlive()) {
214: if (monitor.isCanceled()) {
215: connector.stopListening(map);
216: p.destroy();
217: return;
218: }
219: try {
220: p.exitValue();
221: // process has terminated - stop waiting for a connection
222: try {
223: connector.stopListening(map);
224: } catch (IOException e) {
225: // expected
226: }
227: checkErrorMessage(process);
228: } catch (IllegalThreadStateException e) {
229: // expected while process is alive
230: }
231: try {
232: Thread.sleep(100);
233: } catch (InterruptedException e) {
234: }
235: }
236:
237: Exception ex = runnable.getException();
238: if (ex instanceof IllegalConnectorArgumentsException) {
239: throw (IllegalConnectorArgumentsException) ex;
240: }
241: if (ex instanceof InterruptedIOException) {
242: throw (InterruptedIOException) ex;
243: }
244: if (ex instanceof IOException) {
245: throw (IOException) ex;
246: }
247:
248: VirtualMachine vm = runnable
249: .getVirtualMachine();
250: if (vm != null) {
251: DroolsDebugModel.newDebugTarget(launch, vm,
252: renderDebugTarget(config
253: .getClassToLaunch(), port),
254: process, true, false, config
255: .isResumeOnStartup());
256: subMonitor.worked(1);
257: subMonitor.done();
258: }
259: return;
260: } catch (InterruptedIOException e) {
261: checkErrorMessage(process);
262:
263: // timeout, consult status handler if there is one
264: IStatus status = new Status(
265: IStatus.ERROR,
266: LaunchingPlugin.getUniqueIdentifier(),
267: IJavaLaunchConfigurationConstants.ERR_VM_CONNECT_TIMEOUT,
268: "", e); //$NON-NLS-1$
269: IStatusHandler handler = DebugPlugin
270: .getDefault().getStatusHandler(status);
271:
272: retry = false;
273: if (handler == null) {
274: // if there is no handler, throw the exception
275: throw new CoreException(status);
276: }
277: Object result = handler.handleStatus(status,
278: this );
279: if (result instanceof Boolean) {
280: retry = ((Boolean) result).booleanValue();
281: }
282: }
283: } while (retry);
284: } finally {
285: connector.stopListening(map);
286: }
287: } catch (IOException e) {
288: abort(
289: LaunchingMessages.StandardVMDebugger_Couldn__t_connect_to_VM_4,
290: e,
291: IJavaLaunchConfigurationConstants.ERR_CONNECTION_FAILED);
292: } catch (IllegalConnectorArgumentsException e) {
293: abort(
294: LaunchingMessages.StandardVMDebugger_Couldn__t_connect_to_VM_5,
295: e,
296: IJavaLaunchConfigurationConstants.ERR_CONNECTION_FAILED);
297: }
298: if (p != null) {
299: p.destroy();
300: }
301: }
302:
303: private double getJavaVersion() {
304: LibraryInfo libInfo = LaunchingPlugin
305: .getLibraryInfo(fVMInstance.getInstallLocation()
306: .getAbsolutePath());
307: if (libInfo == null) {
308: return 0D;
309: }
310: String version = libInfo.getVersion();
311: int index = version.indexOf("."); //$NON-NLS-1$
312: int nextIndex = version.indexOf(".", index + 1); //$NON-NLS-1$
313: try {
314: if (index > 0 && nextIndex > index) {
315: return Double.parseDouble(version.substring(0,
316: nextIndex));
317: }
318: return Double.parseDouble(version);
319: } catch (NumberFormatException e) {
320: return 0D;
321: }
322:
323: }
324:
325: protected void checkErrorMessage(IProcess process)
326: throws CoreException {
327: IStreamsProxy streamsProxy = process.getStreamsProxy();
328: if (streamsProxy != null) {
329: String errorMessage = streamsProxy.getErrorStreamMonitor()
330: .getContents();
331: if (errorMessage.length() == 0) {
332: errorMessage = streamsProxy.getOutputStreamMonitor()
333: .getContents();
334: }
335: if (errorMessage.length() != 0) {
336: abort(
337: errorMessage,
338: null,
339: IJavaLaunchConfigurationConstants.ERR_VM_LAUNCH_ERROR);
340: }
341: }
342: }
343:
344: protected void specifyArguments(Map map, int portNumber) {
345: // XXX: Revisit - allows us to put a quote (") around the classpath
346: Connector.IntegerArgument port = (Connector.IntegerArgument) map
347: .get("port"); //$NON-NLS-1$
348: port.setValue(portNumber);
349:
350: Connector.IntegerArgument timeoutArg = (Connector.IntegerArgument) map
351: .get("timeout"); //$NON-NLS-1$
352: if (timeoutArg != null) {
353: int timeout = JavaRuntime.getPreferences().getInt(
354: JavaRuntime.PREF_CONNECT_TIMEOUT);
355: timeoutArg.setValue(timeout);
356: }
357: }
358:
359: protected ListeningConnector getConnector() {
360: List connectors = Bootstrap.virtualMachineManager()
361: .listeningConnectors();
362: for (int i = 0; i < connectors.size(); i++) {
363: ListeningConnector c = (ListeningConnector) connectors
364: .get(i);
365: if ("com.sun.jdi.SocketListen".equals(c.name())) //$NON-NLS-1$
366: return c;
367: }
368: return null;
369: }
370:
371: }
|