1 /*
2  * Copyright (c) 2003, 2018, 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.HashMap;
31 import java.util.Map;
32 
33 import com.sun.jdi.Bootstrap;
34 import com.sun.jdi.VirtualMachine;
35 import com.sun.jdi.connect.Connector;
36 import com.sun.jdi.connect.IllegalConnectorArgumentsException;
37 import com.sun.jdi.connect.ListeningConnector;
38 import com.sun.jdi.connect.Transport;
39 import com.sun.jdi.connect.spi.Connection;
40 import com.sun.jdi.connect.spi.TransportService;
41 
42 /*
43  * A ListeningConnector to listen for connections from target VM
44  * using the configured transport service
45  */
46 public class GenericListeningConnector
47         extends ConnectorImpl implements ListeningConnector
48 {
49     static final String ARG_ADDRESS = "address";
50     static final String ARG_TIMEOUT = "timeout";
51 
52     Map<Map<String,? extends Connector.Argument>, TransportService.ListenKey>  listenMap;
53     TransportService transportService;
54     Transport transport;
55 
56     /**
57      * Initialize a new instance of this connector. The connector
58      * encapsulates a transport service, has a "timeout" connector argument,
59      * and optionally an "address" connector argument.
60      */
GenericListeningConnector(TransportService ts, boolean addAddressArgument)61     private GenericListeningConnector(TransportService ts,
62                                       boolean addAddressArgument)
63     {
64         transportService = ts;
65         transport = new Transport() {
66                 public String name() {
67                     return transportService.name();
68                 }
69             };
70 
71         if (addAddressArgument) {
72             addStringArgument(
73                 ARG_ADDRESS,
74                 getString("generic_listening.address.label"),
75                 getString("generic_listening.address"),
76                 "",
77                 false);
78         }
79 
80         addIntegerArgument(
81                 ARG_TIMEOUT,
82                 getString("generic_listening.timeout.label"),
83                 getString("generic_listening.timeout"),
84                 "",
85                 false,
86                 0, Integer.MAX_VALUE);
87 
88         listenMap = new HashMap<Map<String, ? extends Connector.Argument>, TransportService.ListenKey>(10);
89     }
90 
91     /**
92      * Initialize a new instance of this connector. This constructor is used
93      * when sub-classing - the resulting connector will a "timeout" connector
94      * argument.
95      */
GenericListeningConnector(TransportService ts)96     protected GenericListeningConnector(TransportService ts) {
97         this(ts, false);
98     }
99 
100     /**
101      * Create an instance of this Connector. The resulting ListeningConnector will
102      * have "address" and "timeout" connector arguments.
103      */
create(TransportService ts)104     public static GenericListeningConnector create(TransportService ts) {
105         return new GenericListeningConnector(ts, true);
106     }
107 
startListening(String address, Map<String,? extends Connector.Argument> args)108     public String startListening(String address, Map<String,? extends Connector.Argument> args)
109         throws IOException, IllegalConnectorArgumentsException
110     {
111         TransportService.ListenKey listener = listenMap.get(args);
112         if (listener != null) {
113            throw new IllegalConnectorArgumentsException("Already listening",
114                new ArrayList<>(args.keySet()));
115         }
116         listener = transportService.startListening(address);
117         updateArgumentMapIfRequired(args, listener);
118         listenMap.put(args, listener);
119         return listener.address();
120     }
121 
122     public String
startListening(Map<String, ? extends Connector.Argument> args)123         startListening(Map<String, ? extends Connector.Argument> args)
124         throws IOException, IllegalConnectorArgumentsException
125     {
126         String address = argument(ARG_ADDRESS, args).value();
127         return startListening(address, args);
128     }
129 
stopListening(Map<String, ? extends Connector.Argument> args)130     public void stopListening(Map<String, ? extends Connector.Argument> args)
131         throws IOException, IllegalConnectorArgumentsException
132     {
133         TransportService.ListenKey listener = listenMap.get(args);
134         if (listener == null) {
135            throw new IllegalConnectorArgumentsException("Not listening",
136                new ArrayList<>(args.keySet()));
137         }
138         transportService.stopListening(listener);
139         listenMap.remove(args);
140     }
141 
142     public VirtualMachine
accept(Map<String, ? extends Connector.Argument> args)143         accept(Map<String, ? extends Connector.Argument> args)
144         throws IOException, IllegalConnectorArgumentsException
145     {
146         String ts = argument(ARG_TIMEOUT, args).value();
147         int timeout = 0;
148         if (ts.length() > 0) {
149             timeout = Integer.decode(ts).intValue();
150         }
151 
152         TransportService.ListenKey listener = listenMap.get(args);
153         Connection connection;
154         if (listener != null) {
155             connection = transportService.accept(listener, timeout, 0);
156         } else {
157             /*
158              * Keep compatibility with previous releases - if the
159              * debugger hasn't called startListening then we do a
160              * once-off accept
161              */
162              startListening(args);
163              listener = listenMap.get(args);
164              assert listener != null;
165              connection = transportService.accept(listener, timeout, 0);
166              stopListening(args);
167         }
168         return Bootstrap.virtualMachineManager().createVirtualMachine(connection);
169     }
170 
supportsMultipleConnections()171     public boolean supportsMultipleConnections() {
172         return transportService.capabilities().supportsMultipleConnections();
173     }
174 
name()175     public String name() {
176         return transport.name() + "Listen";
177     }
178 
description()179     public String description() {
180         return transportService.description();
181     }
182 
transport()183     public Transport transport() {
184         return transport;
185     }
186 
updateArgumentMapIfRequired( Map<String, ? extends Connector.Argument> args, TransportService.ListenKey listener)187     protected void updateArgumentMapIfRequired(
188         Map<String, ? extends Connector.Argument> args, TransportService.ListenKey listener) {
189     }
190 
191 }
192