1 /*
  2  * Copyright (c) 1998, 2021, 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.util.ArrayList;
 30 import java.util.Collections;
 31 import java.util.Iterator;
 32 import java.util.List;
 33 import java.util.ResourceBundle;
 34 import java.util.ServiceLoader;
 35 
 36 import com.sun.jdi.JDIPermission;
 37 import com.sun.jdi.VMDisconnectedException;
 38 import com.sun.jdi.VirtualMachine;
 39 import com.sun.jdi.VirtualMachineManager;
 40 import com.sun.jdi.connect.AttachingConnector;
 41 import com.sun.jdi.connect.Connector;
 42 import com.sun.jdi.connect.LaunchingConnector;
 43 import com.sun.jdi.connect.ListeningConnector;
 44 import com.sun.jdi.connect.spi.Connection;
 45 import com.sun.jdi.connect.spi.TransportService;
 46 
 47 /* Public for use by com.sun.jdi.Bootstrap */
 48 public class VirtualMachineManagerImpl implements VirtualMachineManagerService {
 49     private List<Connector> connectors = new ArrayList<>();
 50     private LaunchingConnector defaultConnector = null;
 51     private List<VirtualMachine> targets = new ArrayList<>();
 52     private final ThreadGroup mainGroupForJDI;
 53     private ResourceBundle messages = null;
 54     private int vmSequenceNumber = 0;
 55     private static final int majorVersion = Runtime.version().feature();
 56     private static final int minorVersion = 0;
 57 
 58     private static final Object lock = new Object();
 59     private static VirtualMachineManagerImpl vmm;
 60 
 61     public static VirtualMachineManager virtualMachineManager() {
 62         @SuppressWarnings("removal")
 63         SecurityManager sm = System.getSecurityManager();
 64         if (sm != null) {
 65             JDIPermission vmmPermission =
 66                 new JDIPermission("virtualMachineManager");
 67             sm.checkPermission(vmmPermission);
 68         }
 69         synchronized (lock) {
 70             if (vmm == null) {
 71                 vmm = new VirtualMachineManagerImpl();
 72             }
 73         }
 74         return vmm;
 75     }
 76 
 77     protected VirtualMachineManagerImpl() {
 78 
 79         /*
 80          * Create a top-level thread group
 81          */
 82         ThreadGroup top = Thread.currentThread().getThreadGroup();
 83         ThreadGroup parent = null;
 84         while ((parent = top.getParent()) != null) {
 85             top = parent;
 86         }
 87         @SuppressWarnings("deprecation")
 88         ThreadGroup group = new ThreadGroup(top, "JDI main");
 89         mainGroupForJDI = group;
 90 
 91         /*
 92          * Load the connectors
 93          */
 94         ServiceLoader<Connector> connectorLoader =
 95             ServiceLoader.load(Connector.class, Connector.class.getClassLoader());
 96 
 97         Iterator<Connector> connectors = connectorLoader.iterator();
 98 
 99         while (connectors.hasNext()) {
100             Connector connector;
101 
102             try {
103                 connector = connectors.next();
104             } catch (ThreadDeath x) {
105                 throw x;
106             } catch (Exception x) {
107                 System.err.println(x);
108                 continue;
109             } catch (Error x) {
110                 System.err.println(x);
111                 continue;
112             }
113 
114             addConnector(connector);
115         }
116 
117         /*
118          * Load any transport services and encapsulate them with
119          * an attaching and listening connector.
120          */
121         ServiceLoader<TransportService> transportLoader =
122             ServiceLoader.load(TransportService.class,
123                                TransportService.class.getClassLoader());
124 
125         Iterator<TransportService> transportServices =
126             transportLoader.iterator();
127 
128         while (transportServices.hasNext()) {
129             TransportService transportService;
130 
131             try {
132                 transportService = transportServices.next();
133             } catch (ThreadDeath x) {
134                 throw x;
135             } catch (Exception x) {
136                 System.err.println(x);
137                 continue;
138             } catch (Error x) {
139                 System.err.println(x);
140                 continue;
141             }
142 
143             addConnector(GenericAttachingConnector.create(transportService));
144             addConnector(GenericListeningConnector.create(transportService));
145         }
146 
147         // no connectors found
148         if (allConnectors().size() == 0) {
149             throw new Error("no Connectors loaded");
150         }
151 
152         // Set the default launcher. In order to be compatible
153         // 1.2/1.3/1.4 we try to make the default launcher
154         // "com.sun.jdi.CommandLineLaunch". If this connector
155         // isn't found then we arbitarly pick the first connector.
156         //
157         boolean found = false;
158         List<LaunchingConnector> launchers = launchingConnectors();
159         for (LaunchingConnector lc: launchers) {
160             if (lc.name().equals("com.sun.jdi.CommandLineLaunch")) {
161                 setDefaultConnector(lc);
162                 found = true;
163                 break;
164             }
165         }
166         if (!found && launchers.size() > 0) {
167             setDefaultConnector(launchers.get(0));
168         }
169     }
170 
171     public LaunchingConnector defaultConnector() {
172         if (defaultConnector == null) {
173             throw new Error("no default LaunchingConnector");
174         }
175         return defaultConnector;
176     }
177 
178     public void setDefaultConnector(LaunchingConnector connector) {
179         defaultConnector = connector;
180     }
181 
182     public List<LaunchingConnector> launchingConnectors() {
183         List<LaunchingConnector> launchingConnectors = new ArrayList<>(connectors.size());
184         for (Connector connector: connectors) {
185             if (connector instanceof LaunchingConnector) {
186                 launchingConnectors.add((LaunchingConnector)connector);
187             }
188         }
189         return Collections.unmodifiableList(launchingConnectors);
190     }
191 
192     public List<AttachingConnector> attachingConnectors() {
193         List<AttachingConnector> attachingConnectors = new ArrayList<>(connectors.size());
194         for (Connector connector: connectors) {
195             if (connector instanceof AttachingConnector) {
196                 attachingConnectors.add((AttachingConnector)connector);
197             }
198         }
199         return Collections.unmodifiableList(attachingConnectors);
200     }
201 
202     public List<ListeningConnector> listeningConnectors() {
203         List<ListeningConnector> listeningConnectors = new ArrayList<>(connectors.size());
204         for (Connector connector: connectors) {
205             if (connector instanceof ListeningConnector) {
206                 listeningConnectors.add((ListeningConnector)connector);
207             }
208         }
209         return Collections.unmodifiableList(listeningConnectors);
210     }
211 
212     public List<Connector> allConnectors() {
213         return Collections.unmodifiableList(connectors);
214     }
215 
216     public List<VirtualMachine> connectedVirtualMachines() {
217         return Collections.unmodifiableList(targets);
218     }
219 
220     public void addConnector(Connector connector) {
221         connectors.add(connector);
222     }
223 
224     public void removeConnector(Connector connector) {
225         connectors.remove(connector);
226     }
227 
228     public synchronized VirtualMachine createVirtualMachine(
229                                         Connection connection,
230                                         Process process) throws IOException {
231 
232         if (!connection.isOpen()) {
233             throw new IllegalStateException("connection is not open");
234         }
235 
236         VirtualMachine vm;
237         try {
238             vm = new VirtualMachineImpl(this, connection, process,
239                                                    ++vmSequenceNumber);
240         } catch (VMDisconnectedException e) {
241             throw new IOException(e.getMessage());
242         }
243         targets.add(vm);
244         return vm;
245     }
246 
247     public VirtualMachine createVirtualMachine(Connection connection) throws IOException {
248         return createVirtualMachine(connection, null);
249     }
250 
251     public void addVirtualMachine(VirtualMachine vm) {
252         targets.add(vm);
253     }
254 
255     void disposeVirtualMachine(VirtualMachine vm) {
256         targets.remove(vm);
257     }
258 
259     public int majorInterfaceVersion() {
260         return majorVersion;
261     }
262 
263     public int minorInterfaceVersion() {
264         return minorVersion;
265     }
266 
267     ThreadGroup mainGroupForJDI() {
268         return mainGroupForJDI;
269     }
270 
271     String getString(String key) {
272         if (messages == null) {
273             messages = ResourceBundle.getBundle("com.sun.tools.jdi.resources.jdi");
274         }
275         return messages.getString(key);
276     }
277 }