001: /*
002: *
003: *
004: * Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: */
026:
027: /*
028: * SocketTransportImpl.java
029: *
030: * Created on April 20, 2001, 6:26 PM
031: */
032:
033: package com.sun.cldchi.tools.memoryprofiler.jdwp;
034:
035: import java.net.*;
036: import java.io.*;
037: import java.util.*;
038:
039: /**
040: * This class implements JDWP socket connection and works in the
041: * background thread.
042: *
043: * @see jdwp.SocketTransport
044: *
045: * @version
046: */
047: class SocketTransportImpl extends Thread {
048:
049: /**
050: * A synchronisation object. It's used for synchronising access
051: * to <code>finished</code> field.
052: *
053: * @see #finished
054: */
055: private static Object sync = new Object();
056:
057: /**
058: * A synchronication object. Using this object a reading
059: * loop waits for invoking <code>receive()</code> method.
060: *
061: * @see#run()
062: * @see#receive()
063: */
064: private static Object sync1 = new Object();
065:
066: /**
067: * A synchronisation object. It's used for synchronising access
068: * to the buffer that contains data that already read and to the
069: * <code>cycleStarted</code> field.
070: *
071: * @see #privateBuffer
072: * @see #cycleStarted
073: */
074: private static Object sync2 = new Object();
075:
076: /**
077: * A host name where VM being debugged is running.
078: */
079: private String serverName;
080:
081: /**
082: * A TCP/IP port number that is used for connecting to VM being debugged.
083: */
084: private int portNumber;
085:
086: /**
087: * Indicates that it's time to finish this thread. May be initiated inside
088: * and outside the thread.
089: */
090: private boolean finished = true;
091:
092: /**
093: * A stream that reads data from the socket.
094: */
095: private DataInputStream inputStream;
096:
097: /**
098: * A stream that writes data to the socket.
099: */
100: private DataOutputStream outputStream;
101:
102: /**
103: * A socket that provides JDWP connection.
104: */
105: private Socket socket;
106:
107: /**
108: * A reference to the outer object ahere is to put
109: * an information about received JDWP replies.
110: *
111: * @see jdwp.Transport#Replies
112: */
113: private Vector replies;
114:
115: /**
116: * An internal structure for temporary keeping of received JDWP
117: * replies. Later this data is copied to <code>replies</code> vector.
118: *
119: * @see #receive()
120: * @see #replies
121: */
122: private Vector repliesInternal = new Vector();
123:
124: /**
125: * If this field is set to <code>-1</code> a normal JDWP packet
126: * is expected. Otherwise it indicates that handshake is expected and
127: * is value equals to expected size of handshake string.
128: *
129: * @see #run()
130: * @see #startHandShake(int)
131: */
132: private int expectedSize = -1;
133:
134: /**
135: * A current position in the buffer that contains received handshake
136: * string.
137: *
138: * @see #startHandShake(int)
139: */
140: private int currentInPrivateBuffer = 0;
141:
142: /**
143: * An expected size of received handshake string.
144: *
145: * @see #startHandShake(int)
146: */
147: private int handShakeSize;
148:
149: /**
150: * A buffer that contains a received handshake string.
151: *
152: * @see #startHandShake(int)
153: */
154: private byte[] privateBuffer = new byte[1024];
155:
156: /**
157: * Indicates that the reading thread doesn't sleep but processes reading
158: * of data. Used by handshake procedure to wait until receiving handshake
159: * string.
160: *
161: * @see #startHandShake(int)
162: */
163: private boolean cycleStarted = false;
164:
165: /**
166: * Creates a new <code>SocketTransportImpl</code> object.
167: */
168: public SocketTransportImpl() {
169: super ();
170: }
171:
172: /**
173: * Intializes some internal fields of the object. Need to be invoked
174: * immediately after constructor.
175: *
176: * @param serverName a hostname where VM being debugged is running
177: * @param portNumber a TCP/IP port number that should be used for connecting
178: * with VM being debugged
179: * @param replies a vector where it's necessary to put received JDWP
180: * replies
181: */
182: public void initAsClient(String serverName, int portNumber,
183: Vector replies) throws IOException {
184: this .serverName = serverName;
185: this .portNumber = portNumber;
186: this .replies = replies;
187: }
188:
189: /**
190: * It's an entry point of background thread. This method processes
191: * reading of handshake and normal JDWP packets and stores the received
192: * values in <code>repliesInternal</code> vector.
193: * Also this method establishes JDWP connection.
194: * The method works in a
195: * loop until <code>finished</code> field becomes <code>true</code>.
196: * After this the JDWP connections terminates.
197: */
198: public void run() {
199: //setPriority(MIN_PRIORITY);
200: synchronized (sync) {
201: finished = false;
202: }
203: byte[] buf = new byte[100000];
204: try {
205: attachToServer();
206: boolean timeToQuit = false;
207: synchronized (sync) {
208: timeToQuit = finished;
209: }
210: synchronized (this ) {
211: notifyAll();
212: }
213: while (!timeToQuit) {
214: synchronized (sync1) {
215: sync1.wait();
216: }
217: synchronized (sync2) {
218: cycleStarted = true;
219: }
220:
221: if (expectedSize != -1) {
222: //read handshake
223: int bytesRead = 0;
224: while (bytesRead != expectedSize) {
225: bytesRead += inputStream.read(buf, bytesRead,
226: expectedSize - bytesRead);
227: }
228: synchronized (sync2) {
229: for (int i = 0; i < expectedSize; i++) {
230: privateBuffer[i] = buf[i];
231: }
232: expectedSize = -1;
233: }
234: continue;
235: }
236:
237: //System.out.println("read packet");
238:
239: //Read header
240: int bytesRead = 0;
241: while (bytesRead != Packet.PacketHeaderSize) {
242: int bytesReadDuringLastRead = inputStream.read(buf,
243: bytesRead, Packet.PacketHeaderSize
244: - bytesRead);
245: bytesRead += bytesReadDuringLastRead;
246: if (bytesReadDuringLastRead == -1) {
247: throw new IOException("Connection closed");
248: }
249: }
250:
251: //System.out.println("read data");
252:
253: //Read data
254: int size = (((((int) buf[0]) & 0xFF) << 24)
255: | ((((int) buf[1]) & 0xFF) << 16)
256: | ((((int) buf[2]) & 0xFF) << 8) | (((int) buf[3]) & 0xFF));
257: bytesRead = 0;
258: while (bytesRead != size - Packet.PacketHeaderSize) {
259: int bytesReadDuringLastRead = inputStream.read(buf,
260: bytesRead + Packet.PacketHeaderSize, size
261: - Packet.PacketHeaderSize
262: - bytesRead);
263: bytesRead += bytesReadDuringLastRead;
264: if (bytesReadDuringLastRead == -1) {
265: throw new IOException("Connection closed");
266: }
267: }
268:
269: //System.out.println("data read");
270:
271: Reply r = new Reply();
272: r.resetBuffer();
273: r.addBytes(buf, 0, size);
274: synchronized (repliesInternal) {
275: repliesInternal.add(r);
276: }
277:
278: synchronized (sync) {
279: timeToQuit = finished;
280: }
281: }
282: } catch (IOException e) {
283: synchronized (sync) {
284: finished = true;
285: }
286: synchronized (this ) {
287: notifyAll();
288: }
289: } catch (InterruptedException e) {
290: synchronized (sync) {
291: finished = true;
292: }
293: synchronized (this ) {
294: notifyAll();
295: }
296: }
297: synchronized (sync) {
298: finished = true;
299: }
300: doneInternal();
301: }
302:
303: /**
304: * This method sets the <code>finished</code> field that
305: * indicates that the background thread is to be finished.
306: * After this it closes JDWP connection.
307: */
308: public void done() {
309: synchronized (sync) {
310: finished = true;
311: }
312: doneInternal();
313: }
314:
315: /**
316: * Internal method that terminates JDWP connection in fact.
317: */
318: private synchronized void doneInternal() {
319: try {
320: if (socket == null) {
321: return;
322: }
323: socket.close();
324: } catch (IOException e) {
325: }
326: }
327:
328: /**
329: * Stores a portion of received replies in the outer buffer. If no replies
330: * are received since the previous call of this method it does nothing.
331: *
332: * @see #replies
333: * @see #repliesInternal
334: * @see jdwp.Transport#Replies
335: */
336: public void receive() throws IOException {
337: //System.out.println("receive");
338: synchronized (sync) {
339: if (finished) {
340: throw new SocketException("Connection closed");
341: }
342: }
343: synchronized (sync1) {
344: sync1.notifyAll();
345: }
346: synchronized (repliesInternal) {
347: replies.addAll(repliesInternal);
348: repliesInternal.clear();
349: }
350: }
351:
352: /**
353: * Writes a specified byte into the socket.
354: *
355: * @param b a byte to be written
356: */
357: public void write(int b) throws IOException {
358: outputStream.write(b);
359: //System.out.println("Written byte " + b);
360: }
361:
362: /**
363: * Establishes TCP/IP connection to the VM being debugged.
364: * <p>
365: * NOTE: It's one of few methods in KJDB that produces non-debug
366: * output directly. It's not good. For non-debug output classes
367: * must use <code>kdb.Log</code> class. So may be it's a good idea
368: * to remove the message this method prints.
369: */
370: public void attachToServer() throws IOException,
371: UnknownHostException {
372: socket = new Socket(serverName, portNumber);
373: Tools.wait(100);
374: System.out.println("Socket Created.");
375: socket.setTcpNoDelay(true);
376: inputStream = new DataInputStream(socket.getInputStream());
377: outputStream = new DataOutputStream(socket.getOutputStream());
378: }
379:
380: /**
381: * Initiates handshake procedure that is physically performed
382: * by <code>run()</code> method and waits for <code>run()</code>
383: * method's sterting the waiting for reply. After this it exits.
384: * This procedure is performed immediately after establishing TCP/IP
385: * connection and consist on sending and receiving "JDWP-Handshake" string.
386: *
387: * @param an expected length of received handshake string
388: */
389: public void startHandShake(int len) {
390: currentInPrivateBuffer = 0;
391: expectedSize = len;
392: handShakeSize = len;
393: boolean b = true;
394: do {
395: synchronized (sync1) {
396: sync1.notifyAll();
397: }
398: synchronized (sync2) {
399: b = !cycleStarted;
400: }
401: } while (b);
402: synchronized (sync2) {
403: cycleStarted = false;
404: }
405: }
406:
407: /**
408: * Reads next byte from the buffer that contains received
409: * handshake string.
410: *
411: * @return next byte from the buffer that contains received
412: * handshake string
413: */
414: public int readNextFromPrivateBuffer() throws IOException {
415: synchronized (sync) {
416: if (finished) {
417: throw new SocketException("Connection closed");
418: }
419: }
420: //System.out.println("read");
421: synchronized (sync2) {
422: return privateBuffer[currentInPrivateBuffer++];
423: }
424: }
425:
426: /**
427: * Returns the number of bytes that are available in the buffer
428: * that that contains received handshake string.
429: *
430: * @return the number of bytes that are available in the buffer
431: * that that contains received handshake string
432: */
433: public int availableInPrivateBuffer() throws IOException {
434: synchronized (sync) {
435: if (finished) {
436: throw new SocketException("Connection closed");
437: }
438: }
439: //System.out.println("available");
440: synchronized (sync2) {
441: if (expectedSize == -1) {
442: return handShakeSize;
443: } else {
444: return 0;
445: }
446: }
447: }
448:
449: /**
450: * Checks if the background thread is initialized and is not received
451: * request for terminating yet.
452: *
453: * @return <code>true</code> if the background thread is initialized and
454: * is not received request for terminating yet, <code>false</code>
455: * otherwise
456: */
457: public boolean isStarted() {
458: synchronized (sync) {
459: return !finished;
460: }
461: }
462: }
|