1 /* 2 * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.tools.jdi; 27 28 import com.sun.tools.jdi.*; 29 import com.sun.jdi.connect.*; 30 import com.sun.jdi.connect.spi.*; 31 import com.sun.jdi.VirtualMachine; 32 import java.util.Map; 33 import java.util.HashMap; 34 import java.util.Random; 35 import java.io.IOException; 36 import java.io.File; 37 38 public class SunCommandLineLauncher extends AbstractLauncher implements LaunchingConnector { 39 40 static private final String ARG_HOME = "home"; 41 static private final String ARG_OPTIONS = "options"; 42 static private final String ARG_MAIN = "main"; 43 static private final String ARG_INIT_SUSPEND = "suspend"; 44 static private final String ARG_QUOTE = "quote"; 45 static private final String ARG_VM_EXEC = "vmexec"; 46 47 TransportService transportService; 48 Transport transport; 49 boolean usingSharedMemory = false; 50 51 TransportService transportService() { 52 return transportService; 53 } 54 55 public Transport transport() { 56 return transport; 57 } 58 59 public SunCommandLineLauncher() { 60 super(); 61 62 /** 63 * By default this connector uses either the shared memory 64 * transport or the socket transport 65 */ 66 try { 67 @SuppressWarnings("deprecation") 68 Object o = 69 Class.forName("com.sun.tools.jdi.SharedMemoryTransportService").newInstance(); 70 transportService = (TransportService)o; 71 transport = new Transport() { 72 public String name() { 73 return "dt_shmem"; 74 } 75 }; 76 usingSharedMemory = true; 77 } catch (ClassNotFoundException | 78 UnsatisfiedLinkError | 79 InstantiationException | 80 IllegalAccessException x) { 81 }; 82 if (transportService == null) { 83 transportService = new SocketTransportService(); 84 transport = new Transport() { 85 public String name() { 86 return "dt_socket"; 87 } 88 }; 89 } 90 91 addStringArgument( 92 ARG_HOME, 93 getString("sun.home.label"), 94 getString("sun.home"), 95 System.getProperty("java.home"), 96 false); 97 addStringArgument( 98 ARG_OPTIONS, 99 getString("sun.options.label"), 100 getString("sun.options"), 101 "", 102 false); 103 addStringArgument( 104 ARG_MAIN, 105 getString("sun.main.label"), 106 getString("sun.main"), 107 "", 108 true); 109 110 addBooleanArgument( 111 ARG_INIT_SUSPEND, 112 getString("sun.init_suspend.label"), 113 getString("sun.init_suspend"), 114 true, 115 false); 116 117 addStringArgument( 118 ARG_QUOTE, 119 getString("sun.quote.label"), 120 getString("sun.quote"), 121 "\"", 122 true); 123 addStringArgument( 124 ARG_VM_EXEC, 125 getString("sun.vm_exec.label"), 126 getString("sun.vm_exec"), 127 "java", 128 true); 129 } 130 131 static boolean hasWhitespace(String string) { 132 int length = string.length(); 133 for (int i = 0; i < length; i++) { 134 if (Character.isWhitespace(string.charAt(i))) { 135 return true; 136 } 137 } 138 return false; 139 } 140 141 public VirtualMachine 142 launch(Map<String,? extends Connector.Argument> arguments) 143 throws IOException, IllegalConnectorArgumentsException, 144 VMStartException 145 { 146 VirtualMachine vm; 147 148 String home = argument(ARG_HOME, arguments).value(); 149 String options = argument(ARG_OPTIONS, arguments).value(); 150 String mainClassAndArgs = argument(ARG_MAIN, arguments).value(); 151 boolean wait = ((BooleanArgumentImpl)argument(ARG_INIT_SUSPEND, 152 arguments)).booleanValue(); 153 String quote = argument(ARG_QUOTE, arguments).value(); 154 String exe = argument(ARG_VM_EXEC, arguments).value(); 155 String exePath = null; 156 157 if (quote.length() > 1) { 158 throw new IllegalConnectorArgumentsException("Invalid length", 159 ARG_QUOTE); 160 } 161 162 if ((options.indexOf("-Djava.compiler=") != -1) && 163 (options.toLowerCase().indexOf("-djava.compiler=none") == -1)) { 164 throw new IllegalConnectorArgumentsException("Cannot debug with a JIT compiler", 165 ARG_OPTIONS); 166 } 167 168 /* 169 * Start listening. 170 * If we're using the shared memory transport then we pick a 171 * random address rather than using the (fixed) default. 172 * Random() uses System.currentTimeMillis() as the seed 173 * which can be a problem on windows (many calls to 174 * currentTimeMillis can return the same value), so 175 * we do a few retries if we get an IOException (we 176 * assume the IOException is the filename is already in use.) 177 */ 178 TransportService.ListenKey listenKey; 179 if (usingSharedMemory) { 180 Random rr = new Random(); 181 int failCount = 0; 182 while(true) { 183 try { 184 String address = "javadebug" + 185 String.valueOf(rr.nextInt(100000)); 186 listenKey = transportService().startListening(address); 187 break; 188 } catch (IOException ioe) { 189 if (++failCount > 5) { 190 throw ioe; 191 } 192 } 193 } 194 } else { 195 listenKey = transportService().startListening(); 196 } 197 String address = listenKey.address(); 198 199 try { 200 if (home.length() > 0) { 201 exePath = home + File.separator + "bin" + File.separator + exe; 202 } else { 203 exePath = exe; 204 } 205 // Quote only if necessary in case the quote arg value is bogus 206 if (hasWhitespace(exePath)) { 207 exePath = quote + exePath + quote; 208 } 209 210 String xrun = "transport=" + transport().name() + 211 ",address=" + address + 212 ",suspend=" + (wait? 'y' : 'n'); 213 // Quote only if necessary in case the quote arg value is bogus 214 if (hasWhitespace(xrun)) { 215 xrun = quote + xrun + quote; 216 } 217 218 String command = exePath + ' ' + 219 options + ' ' + 220 "-Xdebug " + 221 "-Xrunjdwp:" + xrun + ' ' + 222 mainClassAndArgs; 223 224 // System.err.println("Command: \"" + command + '"'); 225 vm = launch(tokenizeCommand(command, quote.charAt(0)), address, listenKey, 226 transportService()); 227 } finally { 228 transportService().stopListening(listenKey); 229 } 230 231 return vm; 232 } 233 234 public String name() { 235 return "com.sun.jdi.CommandLineLaunch"; 236 } 237 238 public String description() { 239 return getString("sun.description"); 240 241 } 242 }