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