1/*
2 * Copyright (c) 2003, 2017, 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
26package com.sun.tools.jdi;
27
28import java.io.IOException;
29import java.util.ArrayList;
30import java.util.HashMap;
31import java.util.Map;
32
33import com.sun.jdi.Bootstrap;
34import com.sun.jdi.VirtualMachine;
35import com.sun.jdi.connect.Connector;
36import com.sun.jdi.connect.IllegalConnectorArgumentsException;
37import com.sun.jdi.connect.ListeningConnector;
38import com.sun.jdi.connect.Transport;
39import com.sun.jdi.connect.spi.Connection;
40import 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 */
46public 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     */
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     */
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     */
104    public static GenericListeningConnector create(TransportService ts) {
105        return new GenericListeningConnector(ts, true);
106    }
107
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
117        listener = transportService.startListening(address);
118        listenMap.put(args, listener);
119        return listener.address();
120    }
121
122    public String
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
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
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
171    public boolean supportsMultipleConnections() {
172        return transportService.capabilities().supportsMultipleConnections();
173    }
174
175    public String name() {
176        return transport.name() + "Listen";
177    }
178
179    public String description() {
180        return transportService.description();
181    }
182
183    public Transport transport() {
184        return transport;
185    }
186}
187