001: /*******************************************************************************
002: * Copyright (c) 2000, 2007 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: * Ivan Popov - Bug 184211: JDI connectors throw NullPointerException if used separately
011: * from Eclipse
012: *******************************************************************************/package org.eclipse.jdi.internal.connect;
013:
014: import java.io.IOException;
015: import java.io.InterruptedIOException;
016: import java.net.ServerSocket;
017: import java.util.HashMap;
018: import java.util.Map;
019:
020: import org.eclipse.debug.core.DebugPlugin;
021: import org.eclipse.jdi.internal.VirtualMachineImpl;
022: import org.eclipse.jdi.internal.VirtualMachineManagerImpl;
023:
024: import com.ibm.icu.text.MessageFormat;
025: import com.sun.jdi.VirtualMachine;
026: import com.sun.jdi.connect.Connector;
027: import com.sun.jdi.connect.IllegalConnectorArgumentsException;
028: import com.sun.jdi.connect.LaunchingConnector;
029: import com.sun.jdi.connect.VMStartException;
030:
031: public class SocketLaunchingConnectorImpl extends ConnectorImpl
032: implements LaunchingConnector {
033: /** Time that a launched VM is given to connect to us. */
034: private static final int ACCEPT_TIMEOUT = 10 * 1000;
035:
036: /** Home directory of the SDK or runtime environment used to launch the application. */
037: private String fHome;
038: /** Launched VM options. */
039: private String fOptions;
040: /** Main class and arguments, or if -jar is an option, the main jar file and arguments. */
041: private String fMain;
042: /** All threads will be suspended before execution of main. */
043: private boolean fSuspend;
044: /** Name of the Java VM launcher. */
045: private String fLauncher;
046:
047: /**
048: * Creates new SocketAttachingConnectorImpl.
049: */
050: public SocketLaunchingConnectorImpl(
051: VirtualMachineManagerImpl virtualMachineManager) {
052: super (virtualMachineManager);
053:
054: // Create communication protocol specific transport.
055: SocketTransportImpl transport = new SocketTransportImpl();
056: setTransport(transport);
057: }
058:
059: /**
060: * @return Returns the default arguments.
061: */
062: public Map defaultArguments() {
063: HashMap arguments = new HashMap(6);
064:
065: // Home
066: StringArgumentImpl strArg = new StringArgumentImpl(
067: "home", ConnectMessages.SocketLaunchingConnectorImpl_Home_directory_of_the_SDK_or_runtime_environment_used_to_launch_the_application_1, ConnectMessages.SocketLaunchingConnectorImpl_Home_2, false); //$NON-NLS-1$
068: strArg.setValue(System.getProperty("java.home")); //$NON-NLS-1$
069: arguments.put(strArg.name(), strArg);
070:
071: // Options
072: strArg = new StringArgumentImpl(
073: "options", ConnectMessages.SocketLaunchingConnectorImpl_Launched_VM_options_3, ConnectMessages.SocketLaunchingConnectorImpl_Options_4, false); //$NON-NLS-1$
074: arguments.put(strArg.name(), strArg);
075:
076: // Main
077: strArg = new StringArgumentImpl(
078: "main", ConnectMessages.SocketLaunchingConnectorImpl_Main_class_and_arguments__or_if__jar_is_an_option__the_main_jar_file_and_arguments_5, ConnectMessages.SocketLaunchingConnectorImpl_Main_6, true); //$NON-NLS-1$
079: arguments.put(strArg.name(), strArg);
080:
081: // Suspend
082: BooleanArgumentImpl boolArg = new BooleanArgumentImpl(
083: "suspend", ConnectMessages.SocketLaunchingConnectorImpl_All_threads_will_be_suspended_before_execution_of_main_7, ConnectMessages.SocketLaunchingConnectorImpl_Suspend_8, false); //$NON-NLS-1$
084: boolArg.setValue(true);
085: arguments.put(boolArg.name(), boolArg);
086:
087: // Quote
088: strArg = new StringArgumentImpl(
089: "quote", ConnectMessages.SocketLaunchingConnectorImpl_Character_used_to_combine_space_delimited_text_into_a_single_command_line_argument_9, ConnectMessages.SocketLaunchingConnectorImpl_Quote_10, true); //$NON-NLS-1$
090: strArg.setValue("\""); //$NON-NLS-1$
091: arguments.put(strArg.name(), strArg);
092:
093: // Launcher
094: strArg = new StringArgumentImpl(
095: "vmexec", ConnectMessages.SocketLaunchingConnectorImpl_Name_of_the_Java_VM_launcher_11, ConnectMessages.SocketLaunchingConnectorImpl_Launcher_12, true); //$NON-NLS-1$
096: strArg.setValue("java"); //$NON-NLS-1$
097: arguments.put(strArg.name(), strArg);
098:
099: return arguments;
100: }
101:
102: /**
103: * @return Returns a short identifier for the connector.
104: */
105: public String name() {
106: return "com.sun.jdi.CommandLineLaunch"; //$NON-NLS-1$
107: }
108:
109: /**
110: * @return Returns a human-readable description of this connector and its purpose.
111: */
112: public String description() {
113: return ConnectMessages.SocketLaunchingConnectorImpl_Launches_target_using_Sun_Java_VM_command_line_and_attaches_to_it_13;
114: }
115:
116: /**
117: * Retrieves connection arguments.
118: */
119: private void getConnectionArguments(Map connectionArgs)
120: throws IllegalConnectorArgumentsException {
121: String attribute = ""; //$NON-NLS-1$
122: try {
123: attribute = "home"; //$NON-NLS-1$
124: fHome = ((Connector.StringArgument) connectionArgs
125: .get(attribute)).value();
126: attribute = "options"; //$NON-NLS-1$
127: fOptions = ((Connector.StringArgument) connectionArgs
128: .get(attribute)).value();
129: attribute = "main"; //$NON-NLS-1$
130: fMain = ((Connector.StringArgument) connectionArgs
131: .get(attribute)).value();
132: attribute = "suspend"; //$NON-NLS-1$
133: fSuspend = ((Connector.BooleanArgument) connectionArgs
134: .get(attribute)).booleanValue();
135: attribute = "quote"; //$NON-NLS-1$
136: ((Connector.StringArgument) connectionArgs.get(attribute))
137: .value();
138: attribute = "vmexec"; //$NON-NLS-1$
139: fLauncher = ((Connector.StringArgument) connectionArgs
140: .get(attribute)).value();
141: } catch (ClassCastException e) {
142: throw new IllegalConnectorArgumentsException(
143: ConnectMessages.SocketLaunchingConnectorImpl_Connection_argument_is_not_of_the_right_type_14,
144: attribute);
145: } catch (NullPointerException e) {
146: throw new IllegalConnectorArgumentsException(
147: ConnectMessages.SocketLaunchingConnectorImpl_Necessary_connection_argument_is_null_15,
148: attribute);
149: } catch (NumberFormatException e) {
150: throw new IllegalConnectorArgumentsException(
151: ConnectMessages.SocketLaunchingConnectorImpl_Connection_argument_is_not_a_number_16,
152: attribute);
153: }
154: }
155:
156: /**
157: * Launches an application and connects to its VM.
158: * @return Returns a connected Virtual Machine.
159: */
160: public VirtualMachine launch(Map connectionArgs)
161: throws IOException, IllegalConnectorArgumentsException,
162: VMStartException {
163: getConnectionArguments(connectionArgs);
164:
165: // A listening connector is used that waits for a connection of the VM that is started up.
166: // Note that port number zero means that a free port is chosen.
167: SocketListeningConnectorImpl listenConnector = new SocketListeningConnectorImpl(
168: virtualMachineManager());
169: Map args = listenConnector.defaultArguments();
170: ((Connector.IntegerArgument) args.get("timeout")).setValue(ACCEPT_TIMEOUT); //$NON-NLS-1$
171: String address = listenConnector.startListening(args);
172:
173: // String for Executable.
174: String slash = System.getProperty("file.separator"); //$NON-NLS-1$
175: String execString = fHome + slash + "bin" + slash + fLauncher; //$NON-NLS-1$
176:
177: // Add Debug options.
178: execString += " -Xdebug -Xnoagent -Djava.compiler=NONE"; //$NON-NLS-1$
179: execString += " -Xrunjdwp:transport=dt_socket,address=" + address + ",server=n,suspend=" + (fSuspend ? "y" : "n"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
180:
181: // Add User specified options.
182: if (fOptions != null)
183: execString += " " + fOptions; //$NON-NLS-1$
184:
185: // Add Main class.
186: execString += " " + fMain; //$NON-NLS-1$
187:
188: // Start VM.
189: String[] cmdLine = DebugPlugin.parseArguments(execString);
190: Process proc = Runtime.getRuntime().exec(cmdLine);
191:
192: // The accept times out if the VM does not connect.
193: VirtualMachineImpl virtualMachine;
194: try {
195: virtualMachine = (VirtualMachineImpl) listenConnector
196: .accept(args);
197: } catch (InterruptedIOException e) {
198: proc.destroy();
199: String message = MessageFormat
200: .format(
201: ConnectMessages.SocketLaunchingConnectorImpl_VM_did_not_connect_within_given_time___0__ms_1,
202: new String[] { ((Connector.IntegerArgument) args
203: .get("timeout")).value() }); //$NON-NLS-1$
204: throw new VMStartException(message, proc);
205: }
206:
207: virtualMachine.setLaunchedProcess(proc);
208: return virtualMachine;
209: }
210:
211: /**
212: * Returns a free port number on localhost, or -1 if unable to find a free port.
213: *
214: * @return a free port number on localhost, or -1 if unable to find a free port
215: * @since 3.2
216: */
217: public static int findFreePort() {
218: ServerSocket socket = null;
219: try {
220: socket = new ServerSocket(0);
221: return socket.getLocalPort();
222: } catch (IOException e) {
223: } finally {
224: if (socket != null) {
225: try {
226: socket.close();
227: } catch (IOException e) {
228: }
229: }
230: }
231: return -1;
232: }
233: }
|