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             Class<?> c = Class.forName("com.sun.tools.jdi.SharedMemoryTransportService");
  68             transportService = (TransportService)c.newInstance();
  69             transport = new Transport() {
  70                 public String name() {
  71                     return "dt_shmem";
  72                 }
  73             };
  74             usingSharedMemory = true;
  75         } catch (ClassNotFoundException x) {
  76         } catch (UnsatisfiedLinkError x) {
  77         } catch (InstantiationException x) {
  78         } catch (IllegalAccessException x) {
  79         };
  80         if (transportService == null) {
  81             transportService = new SocketTransportService();
  82             transport = new Transport() {
  83                 public String name() {
  84                     return "dt_socket";
  85                 }
  86             };
  87         }
  88 
  89         addStringArgument(
  90                 ARG_HOME,
  91                 getString("sun.home.label"),
  92                 getString("sun.home"),
  93                 System.getProperty("java.home"),
  94                 false);
  95         addStringArgument(
  96                 ARG_OPTIONS,
  97                 getString("sun.options.label"),
  98                 getString("sun.options"),
  99                 "",
 100                 false);
 101         addStringArgument(
 102                 ARG_MAIN,
 103                 getString("sun.main.label"),
 104                 getString("sun.main"),
 105                 "",
 106                 true);
 107 
 108         addBooleanArgument(
 109                 ARG_INIT_SUSPEND,
 110                 getString("sun.init_suspend.label"),
 111                 getString("sun.init_suspend"),
 112                 true,
 113                 false);
 114 
 115         addStringArgument(
 116                 ARG_QUOTE,
 117                 getString("sun.quote.label"),
 118                 getString("sun.quote"),
 119                 "\"",
 120                 true);
 121         addStringArgument(
 122                 ARG_VM_EXEC,
 123                 getString("sun.vm_exec.label"),
 124                 getString("sun.vm_exec"),
 125                 "java",
 126                 true);
 127     }
 128 
 129     static boolean hasWhitespace(String string) {
 130         int length = string.length();
 131         for (int i = 0; i < length; i++) {
 132             if (Character.isWhitespace(string.charAt(i))) {
 133                 return true;
 134             }
 135         }
 136         return false;
 137     }
 138 
 139     public VirtualMachine
 140         launch(Map<String,? extends Connector.Argument> arguments)
 141         throws IOException, IllegalConnectorArgumentsException,
 142                VMStartException
 143     {
 144         VirtualMachine vm;
 145 
 146         String home = argument(ARG_HOME, arguments).value();
 147         String options = argument(ARG_OPTIONS, arguments).value();
 148         String mainClassAndArgs = argument(ARG_MAIN, arguments).value();
 149         boolean wait = ((BooleanArgumentImpl)argument(ARG_INIT_SUSPEND,
 150                                                   arguments)).booleanValue();
 151         String quote = argument(ARG_QUOTE, arguments).value();
 152         String exe = argument(ARG_VM_EXEC, arguments).value();
 153         String exePath = null;
 154 
 155         if (quote.length() > 1) {
 156             throw new IllegalConnectorArgumentsException("Invalid length",
 157                                                          ARG_QUOTE);
 158         }
 159 
 160         if ((options.indexOf("-Djava.compiler=") != -1) &&
 161             (options.toLowerCase().indexOf("-djava.compiler=none") == -1)) {
 162             throw new IllegalConnectorArgumentsException("Cannot debug with a JIT compiler",
 163                                                          ARG_OPTIONS);
 164         }
 165 
 166         /*
 167          * Start listening.
 168          * If we're using the shared memory transport then we pick a
 169          * random address rather than using the (fixed) default.
 170          * Random() uses System.currentTimeMillis() as the seed
 171          * which can be a problem on windows (many calls to
 172          * currentTimeMillis can return the same value), so
 173          * we do a few retries if we get an IOException (we
 174          * assume the IOException is the filename is already in use.)
 175          */
 176         TransportService.ListenKey listenKey;
 177         if (usingSharedMemory) {
 178             Random rr = new Random();
 179             int failCount = 0;
 180             while(true) {
 181                 try {
 182                     String address = "javadebug" +
 183                         String.valueOf(rr.nextInt(100000));
 184                     listenKey = transportService().startListening(address);
 185                     break;
 186                 } catch (IOException ioe) {
 187                     if (++failCount > 5) {
 188                         throw ioe;
 189                     }
 190                 }
 191             }
 192         } else {
 193             listenKey = transportService().startListening();
 194         }
 195         String address = listenKey.address();
 196 
 197         try {
 198             if (home.length() > 0) {
 199                 String os_arch = System.getProperty("os.arch");
 200                 if ("SunOS".equals(System.getProperty("os.name"))) {
 201                     exePath = home + File.separator + "bin" + File.separator + exe;
 202                 }
 203             } else {
 204                 exePath = exe;
 205             }
 206             // Quote only if necessary in case the quote arg value is bogus
 207             if (hasWhitespace(exePath)) {
 208                 exePath = quote + exePath + quote;
 209             }
 210 
 211             String xrun = "transport=" + transport().name() +
 212                           ",address=" + address +
 213                           ",suspend=" + (wait? 'y' : 'n');
 214             // Quote only if necessary in case the quote arg value is bogus
 215             if (hasWhitespace(xrun)) {
 216                 xrun = quote + xrun + quote;
 217             }
 218 
 219             String command = exePath + ' ' +
 220                              options + ' ' +
 221                              "-Xdebug " +
 222                              "-Xrunjdwp:" + xrun + ' ' +
 223                              mainClassAndArgs;
 224 
 225             // System.err.println("Command: \"" + command + '"');
 226             vm = launch(tokenizeCommand(command, quote.charAt(0)), address, listenKey,
 227                         transportService());
 228         } finally {
 229             transportService().stopListening(listenKey);
 230         }
 231 
 232         return vm;
 233     }
 234 
 235     public String name() {
 236         return "com.sun.jdi.CommandLineLaunch";
 237     }
 238 
 239     public String description() {
 240         return getString("sun.description");
 241 
 242     }
 243 }