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 
transportService()51     TransportService transportService() {
52         return transportService;
53     }
54 
transport()55     public Transport transport() {
56         return transport;
57     }
58 
SunCommandLineLauncher()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 
hasWhitespace(String string)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
launch(Map<String,? extends Connector.Argument> arguments)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                 exePath = home + File.separator + "bin" + File.separator + exe;
200             } else {
201                 exePath = exe;
202             }
203             // Quote only if necessary in case the quote arg value is bogus
204             if (hasWhitespace(exePath)) {
205                 exePath = quote + exePath + quote;
206             }
207 
208             String xrun = "transport=" + transport().name() +
209                           ",address=" + address +
210                           ",suspend=" + (wait? 'y' : 'n');
211             // Quote only if necessary in case the quote arg value is bogus
212             if (hasWhitespace(xrun)) {
213                 xrun = quote + xrun + quote;
214             }
215 
216             String command = exePath + ' ' +
217                              options + ' ' +
218                              "-Xdebug " +
219                              "-Xrunjdwp:" + xrun + ' ' +
220                              mainClassAndArgs;
221 
222             // System.err.println("Command: \"" + command + '"');
223             vm = launch(tokenizeCommand(command, quote.charAt(0)), address, listenKey,
224                         transportService());
225         } finally {
226             transportService().stopListening(listenKey);
227         }
228 
229         return vm;
230     }
231 
name()232     public String name() {
233         return "com.sun.jdi.CommandLineLaunch";
234     }
235 
description()236     public String description() {
237         return getString("sun.description");
238 
239     }
240 }
241