001: /*******************************************************************************
002: * Copyright (c) 2000, 2007 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: * Ivan Popov - Bug 184211: JDI connectors throw NullPointerException if used separately
011: * from Eclipse
012: *******************************************************************************/package org.eclipse.jdi.internal.connect;
013:
014: import java.io.DataInputStream;
015: import java.io.EOFException;
016: import java.io.IOException;
017: import java.io.InputStream;
018: import java.io.OutputStream;
019: import java.net.ServerSocket;
020: import java.net.Socket;
021: import java.net.SocketTimeoutException;
022: import java.util.Arrays;
023:
024: import org.eclipse.jdi.TimeoutException;
025:
026: import com.sun.jdi.connect.TransportTimeoutException;
027: import com.sun.jdi.connect.spi.ClosedConnectionException;
028: import com.sun.jdi.connect.spi.Connection;
029: import com.sun.jdi.connect.spi.TransportService;
030:
031: public class SocketTransportService extends TransportService {
032: /** Handshake bytes used just after connecting VM. */
033: private static final byte[] handshakeBytes = "JDWP-Handshake".getBytes(); //$NON-NLS-1$
034:
035: private Capabilities fCapabilities = new Capabilities() {
036: public boolean supportsAcceptTimeout() {
037: return true;
038: }
039:
040: public boolean supportsAttachTimeout() {
041: return true;
042: }
043:
044: public boolean supportsHandshakeTimeout() {
045: return true;
046: }
047:
048: public boolean supportsMultipleConnections() {
049: return false;
050: }
051: };
052:
053: private class SocketListenKey extends ListenKey {
054: private String fAddress;
055:
056: SocketListenKey(String address) {
057: fAddress = address;
058: }
059:
060: /*
061: * (non-Javadoc)
062: *
063: * @see com.sun.jdi.connect.spi.TransportService.ListenKey#address()
064: */
065: public String address() {
066: return fAddress;
067: }
068: }
069:
070: // for attaching connector
071: private Socket fSocket;
072:
073: private InputStream fInput;
074:
075: private OutputStream fOutput;
076:
077: // for listening or accepting connectors
078: private ServerSocket fServerSocket;
079:
080: /*
081: * (non-Javadoc)
082: *
083: * @see com.sun.jdi.connect.spi.TransportService#accept(com.sun.jdi.connect.spi.TransportService.ListenKey,
084: * long, long)
085: */
086: public Connection accept(ListenKey listenKey, long attachTimeout,
087: long handshakeTimeout) throws IOException {
088: if (attachTimeout > 0) {
089: if (attachTimeout > Integer.MAX_VALUE) {
090: attachTimeout = Integer.MAX_VALUE; //approx 25 days!
091: }
092: fServerSocket.setSoTimeout((int) attachTimeout);
093: }
094: try {
095: fSocket = fServerSocket.accept();
096: } catch (SocketTimeoutException e) {
097: throw new TransportTimeoutException();
098: }
099: fInput = fSocket.getInputStream();
100: fOutput = fSocket.getOutputStream();
101: performHandshake(fInput, fOutput, handshakeTimeout);
102: return new SocketConnection(this );
103: }
104:
105: /*
106: * (non-Javadoc)
107: *
108: * @see com.sun.jdi.connect.spi.TransportService#attach(java.lang.String,
109: * long, long)
110: */
111: public Connection attach(String address, long attachTimeout,
112: long handshakeTimeout) throws IOException {
113: String[] strings = address.split(":"); //$NON-NLS-1$
114: String host = "localhost"; //$NON-NLS-1$
115: int port = 0;
116: if (strings.length == 2) {
117: host = strings[0];
118: port = Integer.parseInt(strings[1]);
119: } else {
120: port = Integer.parseInt(strings[0]);
121: }
122:
123: return attach(host, port, attachTimeout, handshakeTimeout);
124: }
125:
126: public Connection attach(final String host, final int port,
127: long attachTimeout, final long handshakeTimeout)
128: throws IOException {
129: if (attachTimeout > 0) {
130: if (attachTimeout > Integer.MAX_VALUE) {
131: attachTimeout = Integer.MAX_VALUE; //approx 25 days!
132: }
133: }
134:
135: final IOException[] ex = new IOException[1];
136: Thread attachThread = new Thread(new Runnable() {
137: public void run() {
138: try {
139: fSocket = new Socket(host, port);
140: fInput = fSocket.getInputStream();
141: fOutput = fSocket.getOutputStream();
142: performHandshake(fInput, fOutput, handshakeTimeout);
143: } catch (IOException e) {
144: ex[0] = e;
145: }
146: }
147: }, ConnectMessages.SocketTransportService_0);
148: attachThread.setDaemon(true);
149: attachThread.start();
150: try {
151: attachThread.join(attachTimeout);
152: if (attachThread.isAlive()) {
153: attachThread.interrupt();
154: throw new TimeoutException();
155: }
156: } catch (InterruptedException e) {
157: }
158:
159: if (ex[0] != null) {
160: throw ex[0];
161: }
162:
163: return new SocketConnection(this );
164: }
165:
166: void performHandshake(final InputStream in, final OutputStream out,
167: final long timeout) throws IOException {
168: final IOException[] ex = new IOException[1];
169: final boolean[] handshakeCompleted = new boolean[1];
170:
171: Thread t = new Thread(new Runnable() {
172: public void run() {
173: try {
174: writeHandshake(out);
175: readHandshake(in);
176: handshakeCompleted[0] = true;
177: } catch (IOException e) {
178: ex[0] = e;
179: }
180: }
181: }, ConnectMessages.SocketTransportService_1);
182: t.setDaemon(true);
183: t.start();
184: try {
185: t.join(timeout);
186: } catch (InterruptedException e1) {
187: }
188:
189: if (handshakeCompleted[0])
190: return;
191:
192: try {
193: in.close();
194: out.close();
195: } catch (IOException e) {
196: }
197:
198: if (ex[0] != null)
199: throw ex[0];
200:
201: throw new TransportTimeoutException();
202: }
203:
204: private void readHandshake(InputStream input) throws IOException {
205: try {
206: DataInputStream in = new DataInputStream(input);
207: byte[] handshakeInput = new byte[handshakeBytes.length];
208: in.readFully(handshakeInput);
209: if (!Arrays.equals(handshakeInput, handshakeBytes))
210: throw new IOException("Received invalid handshake"); //$NON-NLS-1$
211: } catch (EOFException e) {
212: throw new ClosedConnectionException();
213: }
214: }
215:
216: private void writeHandshake(OutputStream out) throws IOException {
217: out.write(handshakeBytes);
218: }
219:
220: /*
221: * (non-Javadoc)
222: *
223: * @see com.sun.jdi.connect.spi.TransportService#capabilities()
224: */
225: public Capabilities capabilities() {
226: return fCapabilities;
227: }
228:
229: /*
230: * (non-Javadoc)
231: *
232: * @see com.sun.jdi.connect.spi.TransportService#description()
233: */
234: public String description() {
235: return "org.eclipse.jdt.debug: Socket Implementation of TransportService"; //$NON-NLS-1$
236: }
237:
238: /*
239: * (non-Javadoc)
240: *
241: * @see com.sun.jdi.connect.spi.TransportService#name()
242: */
243: public String name() {
244: return "org.eclipse.jdt.debug_SocketTransportService"; //$NON-NLS-1$
245: }
246:
247: /*
248: * (non-Javadoc)
249: *
250: * @see com.sun.jdi.connect.spi.TransportService#startListening()
251: */
252: public ListenKey startListening() throws IOException {
253: // not used by jdt debug.
254: return startListening(null);
255: }
256:
257: /*
258: * (non-Javadoc)
259: *
260: * @see com.sun.jdi.connect.spi.TransportService#startListening(java.lang.String)
261: */
262: public ListenKey startListening(String address) throws IOException {
263: String host = null;
264: int port = 0; // jdt debugger will always specify an address in
265: // the form localhost:port
266: if (address != null) {
267: String[] strings = address.split(":"); //$NON-NLS-1$
268: host = "localhost"; //$NON-NLS-1$
269: if (strings.length == 2) {
270: host = strings[0];
271: port = Integer.parseInt(strings[1]);
272: } else {
273: port = Integer.parseInt(strings[0]);
274: }
275: }
276: if (host == null) {
277: host = "localhost"; //$NON-NLS-1$
278: }
279:
280: fServerSocket = new ServerSocket(port);
281: port = fServerSocket.getLocalPort();
282: ListenKey listenKey = new SocketListenKey(host + ":" + port); //$NON-NLS-1$
283: return listenKey;
284: }
285:
286: /*
287: * (non-Javadoc)
288: *
289: * @see com.sun.jdi.connect.spi.TransportService#stopListening(com.sun.jdi.connect.spi.TransportService.ListenKey)
290: */
291: public void stopListening(ListenKey arg1) throws IOException {
292: if (fServerSocket != null) {
293: try {
294: fServerSocket.close();
295: } catch (IOException e) {
296: }
297: }
298: fServerSocket = null;
299: }
300:
301: public void close() {
302: if (fSocket != null) {
303: try {
304: fSocket.close();
305: } catch (IOException e) {
306: }
307: }
308:
309: fServerSocket = null;
310: fSocket = null;
311: fInput = null;
312: fOutput = null;
313: }
314:
315: /**
316: * @return
317: */
318: public InputStream getInputStream() {
319: return fInput;
320: }
321:
322: /**
323: * @return
324: */
325: public OutputStream getOutputStream() {
326: return fOutput;
327: }
328: }
|