| /* |
| * Copyright (c) 1998, 2004, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| package com.sun.tools.jdi; |
| |
| import com.sun.tools.jdi.*; |
| import com.sun.jdi.connect.*; |
| import com.sun.jdi.connect.spi.*; |
| import com.sun.jdi.VirtualMachine; |
| import java.util.Map; |
| import java.util.HashMap; |
| import java.util.Random; |
| import java.io.IOException; |
| import java.io.File; |
| |
| public class SunCommandLineLauncher extends AbstractLauncher implements LaunchingConnector { |
| |
| static private final String ARG_HOME = "home"; |
| static private final String ARG_OPTIONS = "options"; |
| static private final String ARG_MAIN = "main"; |
| static private final String ARG_INIT_SUSPEND = "suspend"; |
| static private final String ARG_QUOTE = "quote"; |
| static private final String ARG_VM_EXEC = "vmexec"; |
| |
| TransportService transportService; |
| Transport transport; |
| boolean usingSharedMemory = false; |
| |
| TransportService transportService() { |
| return transportService; |
| } |
| |
| public Transport transport() { |
| return transport; |
| } |
| |
| public SunCommandLineLauncher() { |
| super(); |
| |
| /** |
| * By default this connector uses either the shared memory |
| * transport or the socket transport |
| */ |
| try { |
| Class c = Class.forName("com.sun.tools.jdi.SharedMemoryTransportService"); |
| transportService = (TransportService)c.newInstance(); |
| transport = new Transport() { |
| public String name() { |
| return "dt_shmem"; |
| } |
| }; |
| usingSharedMemory = true; |
| } catch (ClassNotFoundException x) { |
| } catch (UnsatisfiedLinkError x) { |
| } catch (InstantiationException x) { |
| } catch (IllegalAccessException x) { |
| }; |
| if (transportService == null) { |
| transportService = new SocketTransportService(); |
| transport = new Transport() { |
| public String name() { |
| return "dt_socket"; |
| } |
| }; |
| } |
| |
| addStringArgument( |
| ARG_HOME, |
| getString("sun.home.label"), |
| getString("sun.home"), |
| System.getProperty("java.home"), |
| false); |
| addStringArgument( |
| ARG_OPTIONS, |
| getString("sun.options.label"), |
| getString("sun.options"), |
| "", |
| false); |
| addStringArgument( |
| ARG_MAIN, |
| getString("sun.main.label"), |
| getString("sun.main"), |
| "", |
| true); |
| |
| addBooleanArgument( |
| ARG_INIT_SUSPEND, |
| getString("sun.init_suspend.label"), |
| getString("sun.init_suspend"), |
| true, |
| false); |
| |
| addStringArgument( |
| ARG_QUOTE, |
| getString("sun.quote.label"), |
| getString("sun.quote"), |
| "\"", |
| true); |
| addStringArgument( |
| ARG_VM_EXEC, |
| getString("sun.vm_exec.label"), |
| getString("sun.vm_exec"), |
| "java", |
| true); |
| } |
| |
| static boolean hasWhitespace(String string) { |
| int length = string.length(); |
| for (int i = 0; i < length; i++) { |
| if (Character.isWhitespace(string.charAt(i))) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| public VirtualMachine |
| launch(Map<String,? extends Connector.Argument> arguments) |
| throws IOException, IllegalConnectorArgumentsException, |
| VMStartException |
| { |
| VirtualMachine vm; |
| |
| String home = argument(ARG_HOME, arguments).value(); |
| String options = argument(ARG_OPTIONS, arguments).value(); |
| String mainClassAndArgs = argument(ARG_MAIN, arguments).value(); |
| boolean wait = ((BooleanArgumentImpl)argument(ARG_INIT_SUSPEND, |
| arguments)).booleanValue(); |
| String quote = argument(ARG_QUOTE, arguments).value(); |
| String exe = argument(ARG_VM_EXEC, arguments).value(); |
| String exePath = null; |
| |
| if (quote.length() > 1) { |
| throw new IllegalConnectorArgumentsException("Invalid length", |
| ARG_QUOTE); |
| } |
| |
| if ((options.indexOf("-Djava.compiler=") != -1) && |
| (options.toLowerCase().indexOf("-djava.compiler=none") == -1)) { |
| throw new IllegalConnectorArgumentsException("Cannot debug with a JIT compiler", |
| ARG_OPTIONS); |
| } |
| |
| /* |
| * Start listening. |
| * If we're using the shared memory transport then we pick a |
| * random address rather than using the (fixed) default. |
| * Random() uses System.currentTimeMillis() as the seed |
| * which can be a problem on windows (many calls to |
| * currentTimeMillis can return the same value), so |
| * we do a few retries if we get an IOException (we |
| * assume the IOException is the filename is already in use.) |
| */ |
| TransportService.ListenKey listenKey; |
| if (usingSharedMemory) { |
| Random rr = new Random(); |
| int failCount = 0; |
| while(true) { |
| try { |
| String address = "javadebug" + |
| String.valueOf(rr.nextInt(100000)); |
| listenKey = transportService().startListening(address); |
| break; |
| } catch (IOException ioe) { |
| if (++failCount > 5) { |
| throw ioe; |
| } |
| } |
| } |
| } else { |
| listenKey = transportService().startListening(); |
| } |
| String address = listenKey.address(); |
| |
| try { |
| if (home.length() > 0) { |
| /* |
| * A wrinkle in the environment: |
| * 64-bit executables are stored under $JAVA_HOME/bin/os_arch |
| * 32-bit executables are stored under $JAVA_HOME/bin |
| */ |
| String os_arch = System.getProperty("os.arch"); |
| if ("SunOS".equals(System.getProperty("os.name")) && |
| ("sparcv9".equals(os_arch) || "amd64".equals(os_arch))) { |
| exePath = home + File.separator + "bin" + File.separator + |
| os_arch + File.separator + exe; |
| } else { |
| exePath = home + File.separator + "bin" + File.separator + exe; |
| } |
| } else { |
| exePath = exe; |
| } |
| // Quote only if necessary in case the quote arg value is bogus |
| if (hasWhitespace(exePath)) { |
| exePath = quote + exePath + quote; |
| } |
| |
| String xrun = "transport=" + transport().name() + |
| ",address=" + address + |
| ",suspend=" + (wait? 'y' : 'n'); |
| // Quote only if necessary in case the quote arg value is bogus |
| if (hasWhitespace(xrun)) { |
| xrun = quote + xrun + quote; |
| } |
| |
| String command = exePath + ' ' + |
| options + ' ' + |
| "-Xdebug " + |
| "-Xrunjdwp:" + xrun + ' ' + |
| mainClassAndArgs; |
| |
| // System.err.println("Command: \"" + command + '"'); |
| vm = launch(tokenizeCommand(command, quote.charAt(0)), address, listenKey, |
| transportService()); |
| } finally { |
| transportService().stopListening(listenKey); |
| } |
| |
| return vm; |
| } |
| |
| public String name() { |
| return "com.sun.jdi.CommandLineLaunch"; |
| } |
| |
| public String description() { |
| return getString("sun.description"); |
| |
| } |
| } |