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         mainGroupForJDI = new ThreadGroup(top, "JDI main");
 88 
 89         /*
 90          * Load the connectors
 91          */
 92         ServiceLoader<Connector> connectorLoader =
 93             ServiceLoader.load(Connector.class, Connector.class.getClassLoader());
 94 
 95         Iterator<Connector> connectors = connectorLoader.iterator();
 96 
 97         while (connectors.hasNext()) {
 98             Connector connector;
 99 
100             try {
101                 connector = connectors.next();
102             } catch (ThreadDeath x) {
103                 throw x;
104             } catch (Exception x) {
105                 System.err.println(x);
106                 continue;
107             } catch (Error x) {
108                 System.err.println(x);
109                 continue;
110             }
111 
112             addConnector(connector);
113         }
114 
115         /*
116          * Load any transport services and encapsulate them with
117          * an attaching and listening connector.
118          */
119         ServiceLoader<TransportService> transportLoader =
120             ServiceLoader.load(TransportService.class,
121                                TransportService.class.getClassLoader());
122 
123         Iterator<TransportService> transportServices =
124             transportLoader.iterator();
125 
126         while (transportServices.hasNext()) {
127             TransportService transportService;
128 
129             try {
130                 transportService = transportServices.next();
131             } catch (ThreadDeath x) {
132                 throw x;
133             } catch (Exception x) {
134                 System.err.println(x);
135                 continue;
136             } catch (Error x) {
137                 System.err.println(x);
138                 continue;
139             }
140 
141             addConnector(GenericAttachingConnector.create(transportService));
142             addConnector(GenericListeningConnector.create(transportService));
143         }
144 
145         // no connectors found
146         if (allConnectors().size() == 0) {
147             throw new Error("no Connectors loaded");
148         }
149 
150         // Set the default launcher. In order to be compatible
151         // 1.2/1.3/1.4 we try to make the default launcher
152         // "com.sun.jdi.CommandLineLaunch". If this connector
153         // isn't found then we arbitarly pick the first connector.
154         //
155         boolean found = false;
156         List<LaunchingConnector> launchers = launchingConnectors();
157         for (LaunchingConnector lc: launchers) {
158             if (lc.name().equals("com.sun.jdi.CommandLineLaunch")) {
159                 setDefaultConnector(lc);
160                 found = true;
161                 break;
162             }
163         }
164         if (!found && launchers.size() > 0) {
165             setDefaultConnector(launchers.get(0));
166         }
167     }
168 
169     public LaunchingConnector defaultConnector() {
170         if (defaultConnector == null) {
171             throw new Error("no default LaunchingConnector");
172         }
173         return defaultConnector;
174     }
175 
176     public void setDefaultConnector(LaunchingConnector connector) {
177         defaultConnector = connector;
178     }
179 
180     public List<LaunchingConnector> launchingConnectors() {
181         List<LaunchingConnector> launchingConnectors = new ArrayList<>(connectors.size());
182         for (Connector connector: connectors) {
183             if (connector instanceof LaunchingConnector) {
184                 launchingConnectors.add((LaunchingConnector)connector);
185             }
186         }
187         return Collections.unmodifiableList(launchingConnectors);
188     }
189 
190     public List<AttachingConnector> attachingConnectors() {
191         List<AttachingConnector> attachingConnectors = new ArrayList<>(connectors.size());
192         for (Connector connector: connectors) {
193             if (connector instanceof AttachingConnector) {
194                 attachingConnectors.add((AttachingConnector)connector);
195             }
196         }
197         return Collections.unmodifiableList(attachingConnectors);
198     }
199 
200     public List<ListeningConnector> listeningConnectors() {
201         List<ListeningConnector> listeningConnectors = new ArrayList<>(connectors.size());
202         for (Connector connector: connectors) {
203             if (connector instanceof ListeningConnector) {
204                 listeningConnectors.add((ListeningConnector)connector);
205             }
206         }
207         return Collections.unmodifiableList(listeningConnectors);
208     }
209 
210     public List<Connector> allConnectors() {
211         return Collections.unmodifiableList(connectors);
212     }
213 
214     public List<VirtualMachine> connectedVirtualMachines() {
215         return Collections.unmodifiableList(targets);
216     }
217 
218     public void addConnector(Connector connector) {
219         connectors.add(connector);
220     }
221 
222     public void removeConnector(Connector connector) {
223         connectors.remove(connector);
224     }
225 
226     public synchronized VirtualMachine createVirtualMachine(
227                                         Connection connection,
228                                         Process process) throws IOException {
229 
230         if (!connection.isOpen()) {
231             throw new IllegalStateException("connection is not open");
232         }
233 
234         VirtualMachine vm;
235         try {
236             vm = new VirtualMachineImpl(this, connection, process,
237                                                    ++vmSequenceNumber);
238         } catch (VMDisconnectedException e) {
239             throw new IOException(e.getMessage());
240         }
241         targets.add(vm);
242         return vm;
243     }
244 
245     public VirtualMachine createVirtualMachine(Connection connection) throws IOException {
246         return createVirtualMachine(connection, null);
247     }
248 
249     public void addVirtualMachine(VirtualMachine vm) {
250         targets.add(vm);
251     }
252 
253     void disposeVirtualMachine(VirtualMachine vm) {
254         targets.remove(vm);
255     }
256 
257     public int majorInterfaceVersion() {
258         return majorVersion;
259     }
260 
261     public int minorInterfaceVersion() {
262         return minorVersion;
263     }
264 
265     ThreadGroup mainGroupForJDI() {
266         return mainGroupForJDI;
267     }
268 
269     String getString(String key) {
270         if (messages == null) {
271             messages = ResourceBundle.getBundle("com.sun.tools.jdi.resources.jdi");
272         }
273         return messages.getString(key);
274     }
275 }