1 /* 2 * Copyright (c) 1999, 2017, 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 java.io.IOException; 29 import java.io.InterruptedIOException; 30 import java.util.ArrayList; 31 import java.util.List; 32 import java.util.Map; 33 import java.util.StringTokenizer; 34 35 import com.sun.jdi.Bootstrap; 36 import com.sun.jdi.InternalException; 37 import com.sun.jdi.VirtualMachine; 38 import com.sun.jdi.VirtualMachineManager; 39 import com.sun.jdi.connect.Connector; 40 import com.sun.jdi.connect.IllegalConnectorArgumentsException; 41 import com.sun.jdi.connect.LaunchingConnector; 42 import com.sun.jdi.connect.VMStartException; 43 import com.sun.jdi.connect.spi.Connection; 44 import com.sun.jdi.connect.spi.TransportService; 45 46 abstract class AbstractLauncher extends ConnectorImpl 47 implements LaunchingConnector 48 { 49 abstract public VirtualMachine launch(Map<String, ? extends Connector.Argument> arguments)50 launch(Map<String, ? extends Connector.Argument> arguments) 51 throws IOException, IllegalConnectorArgumentsException, 52 VMStartException; 53 name()54 abstract public String name(); 55 description()56 abstract public String description(); 57 58 ThreadGroup grp; 59 AbstractLauncher()60 AbstractLauncher() { 61 super(); 62 63 grp = Thread.currentThread().getThreadGroup(); 64 ThreadGroup parent = null; 65 while ((parent = grp.getParent()) != null) { 66 grp = parent; 67 } 68 } 69 tokenizeCommand(String command, char quote)70 String[] tokenizeCommand(String command, char quote) { 71 String quoteStr = String.valueOf(quote); // easier to deal with 72 73 /* 74 * Tokenize the command, respecting the given quote character. 75 */ 76 StringTokenizer tokenizer = new StringTokenizer(command, 77 quote + " \t\r\n\f", 78 true); 79 String quoted = null; 80 String pending = null; 81 List<String> tokenList = new ArrayList<>(); 82 while (tokenizer.hasMoreTokens()) { 83 String token = tokenizer.nextToken(); 84 if (quoted != null) { 85 if (token.equals(quoteStr)) { 86 tokenList.add(quoted); 87 quoted = null; 88 } else { 89 quoted += token; 90 } 91 } else if (pending != null) { 92 if (token.equals(quoteStr)) { 93 quoted = pending; 94 } else if ((token.length() == 1) && 95 Character.isWhitespace(token.charAt(0))) { 96 tokenList.add(pending); 97 } else { 98 throw new InternalException("Unexpected token: " + token); 99 } 100 pending = null; 101 } else { 102 if (token.equals(quoteStr)) { 103 quoted = ""; 104 } else if ((token.length() == 1) && 105 Character.isWhitespace(token.charAt(0))) { 106 // continue 107 } else { 108 pending = token; 109 } 110 } 111 } 112 113 /* 114 * Add final token. 115 */ 116 if (pending != null) { 117 tokenList.add(pending); 118 } 119 120 /* 121 * An unclosed quote at the end of the command. Do an 122 * implicit end quote. 123 */ 124 if (quoted != null) { 125 tokenList.add(quoted); 126 } 127 128 String[] tokenArray = new String[tokenList.size()]; 129 for (int i = 0; i < tokenList.size(); i++) { 130 tokenArray[i] = tokenList.get(i); 131 } 132 return tokenArray; 133 } 134 launch(String[] commandArray, String address, TransportService.ListenKey listenKey, TransportService ts)135 protected VirtualMachine launch(String[] commandArray, String address, 136 TransportService.ListenKey listenKey, 137 TransportService ts) 138 throws IOException, VMStartException { 139 Helper helper = new Helper(commandArray, address, listenKey, ts); 140 helper.launchAndAccept(); 141 142 VirtualMachineManager manager = 143 Bootstrap.virtualMachineManager(); 144 145 return manager.createVirtualMachine(helper.connection(), 146 helper.process()); 147 } 148 149 /** 150 * This class simply provides a context for a single launch and 151 * accept. It provides instance fields that can be used by 152 * all threads involved. This stuff can't be in the Connector proper 153 * because the connector is a singleton and is not specific to any 154 * one launch. 155 */ 156 private class Helper { 157 @SuppressWarnings("unused") 158 private final String address; 159 private TransportService.ListenKey listenKey; 160 private TransportService ts; 161 private final String[] commandArray; 162 private Process process = null; 163 private Connection connection = null; 164 private IOException acceptException = null; 165 private boolean exited = false; 166 Helper(String[] commandArray, String address, TransportService.ListenKey listenKey, TransportService ts)167 Helper(String[] commandArray, String address, TransportService.ListenKey listenKey, 168 TransportService ts) { 169 this.commandArray = commandArray; 170 this.address = address; 171 this.listenKey = listenKey; 172 this.ts = ts; 173 } 174 commandString()175 String commandString() { 176 String str = ""; 177 for (int i = 0; i < commandArray.length; i++) { 178 if (i > 0) { 179 str += " "; 180 } 181 str += commandArray[i]; 182 } 183 return str; 184 } 185 launchAndAccept()186 synchronized void launchAndAccept() throws 187 IOException, VMStartException { 188 189 process = Runtime.getRuntime().exec(commandArray); 190 191 Thread acceptingThread = acceptConnection(); 192 Thread monitoringThread = monitorTarget(); 193 try { 194 while ((connection == null) && 195 (acceptException == null) && 196 !exited) { 197 wait(); 198 } 199 200 if (exited) { 201 throw new VMStartException( 202 "VM initialization failed for: " + commandString(), process); 203 } 204 if (acceptException != null) { 205 // Rethrow the exception in this thread 206 throw acceptException; 207 } 208 } catch (InterruptedException e) { 209 throw new InterruptedIOException("Interrupted during accept"); 210 } finally { 211 acceptingThread.interrupt(); 212 monitoringThread.interrupt(); 213 } 214 } 215 process()216 Process process() { 217 return process; 218 } 219 connection()220 Connection connection() { 221 return connection; 222 } 223 notifyOfExit()224 synchronized void notifyOfExit() { 225 exited = true; 226 notify(); 227 } 228 notifyOfConnection(Connection connection)229 synchronized void notifyOfConnection(Connection connection) { 230 this.connection = connection; 231 notify(); 232 } 233 notifyOfAcceptException(IOException acceptException)234 synchronized void notifyOfAcceptException(IOException acceptException) { 235 this.acceptException = acceptException; 236 notify(); 237 } 238 monitorTarget()239 Thread monitorTarget() { 240 Thread thread = new Thread(grp, "launched target monitor") { 241 public void run() { 242 try { 243 process.waitFor(); 244 /* 245 * Notify waiting thread of VM error termination 246 */ 247 notifyOfExit(); 248 } catch (InterruptedException e) { 249 // Connection has been established, stop monitoring 250 } 251 } 252 }; 253 thread.setDaemon(true); 254 thread.start(); 255 return thread; 256 } 257 acceptConnection()258 Thread acceptConnection() { 259 Thread thread = new Thread(grp, "connection acceptor") { 260 public void run() { 261 try { 262 Connection connection = ts.accept(listenKey, 0, 0); 263 /* 264 * Notify waiting thread of connection 265 */ 266 notifyOfConnection(connection); 267 } catch (InterruptedIOException e) { 268 // VM terminated, stop accepting 269 } catch (IOException e) { 270 // Report any other exception to waiting thread 271 notifyOfAcceptException(e); 272 } 273 } 274 }; 275 thread.setDaemon(true); 276 thread.start(); 277 return thread; 278 } 279 } 280 } 281