Source for gnu.classpath.jdwp.transport.JdwpConnection

   1: /* JdwpConnection.java -- A JDWP-speaking connection
   2:    Copyright (C) 2005, 2006, 2007 Free Software Foundation
   3: 
   4: This file is part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2, or (at your option)
   9: any later version.
  10: 
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; see the file COPYING.  If not, write to the
  18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19: 02110-1301 USA.
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: terms of your choice, provided that you also meet, for each linked
  32: independent module, the terms and conditions of the license of that
  33: module.  An independent module is a module which is not derived from
  34: or based on this library.  If you modify this library, you may extend
  35: this exception to your version of the library, but you are not
  36: obligated to do so.  If you do not wish to do so, delete this
  37: exception statement from your version. */
  38: 
  39: 
  40: package gnu.classpath.jdwp.transport;
  41: 
  42: import gnu.classpath.jdwp.Jdwp;
  43: import gnu.classpath.jdwp.event.Event;
  44: import gnu.classpath.jdwp.event.EventRequest;
  45: 
  46: import java.io.ByteArrayOutputStream;
  47: import java.io.DataInputStream;
  48: import java.io.DataOutputStream;
  49: import java.io.IOException;
  50: import java.util.ArrayList;
  51: import java.util.Arrays;
  52: 
  53: /**
  54:  * A connection via some transport to some JDWP-speaking entity.
  55:  * This is also a thread which handles all communications to/from
  56:  * the debugger. While access to the transport layer may be accessed by
  57:  * several threads, start-up and initialization should not be allowed
  58:  * to occur more than once.
  59:  *
  60:  * <p>This class is also a thread that is responsible for pulling
  61:  * packets off the wire and sticking them in a queue for packet
  62:  * processing threads.
  63:  *
  64:  * @author Keith Seitz (keiths@redhat.com)
  65:  */
  66: public class JdwpConnection
  67:   extends Thread
  68: {
  69:   // The JDWP handshake
  70:   private static final byte[] _HANDSHAKE = {'J', 'D', 'W', 'P', '-', 'H', 'a',
  71:                                             'n', 'd', 's', 'h', 'a', 'k', 'e'};
  72: 
  73:   // Transport method
  74:   private ITransport _transport;
  75: 
  76:   // Command queue
  77:   private ArrayList _commandQueue;
  78: 
  79:   // Shutdown flag
  80:   private boolean _shutdown;
  81: 
  82:   // Input stream from transport
  83:   private DataInputStream _inStream;
  84: 
  85:   // Output stream from transprot
  86:   private DataOutputStream _outStream;
  87: 
  88:   // A buffer used to construct the packet data
  89:   private ByteArrayOutputStream _bytes;
  90: 
  91:   // A DataOutputStream for the byte buffer
  92:   private DataOutputStream _doStream;
  93: 
  94:   /**
  95:    * Creates a new <code>JdwpConnection</code> instance
  96:    *
  97:    * @param transport  the transport to use for communications
  98:    */
  99:   public JdwpConnection (ThreadGroup group, ITransport transport)
 100:   {
 101:     super (group, "JDWP connection thread");
 102:     _transport = transport;
 103:     _commandQueue = new ArrayList ();
 104:     _shutdown = false;
 105:     _bytes = new ByteArrayOutputStream ();
 106:     _doStream = new DataOutputStream (_bytes);
 107:   }
 108: 
 109:   /**
 110:    * Initializes the connection, including connecting
 111:    * to socket or shared memory endpoint
 112:    *
 113:    * @throws TransportException if initialization fails
 114:    */
 115:   public void initialize ()
 116:     throws TransportException
 117:   {
 118:     // Initialize transport (connect socket, e.g.)
 119:     _transport.initialize ();
 120: 
 121:     // Do handshake
 122:     try
 123:       {
 124:         _inStream = new DataInputStream (_transport.getInputStream ());
 125:         _outStream = new DataOutputStream (_transport.getOutputStream ());
 126:         _doHandshake ();
 127:       }
 128:     catch (IOException ioe)
 129:       {
 130:         throw new TransportException (ioe);
 131:       }
 132:   }
 133: 
 134:   /* Does the JDWP handshake -- this should not need synchronization
 135:      because this is called by VM startup code, i.e., no packet
 136:      processing threads have started yet. */
 137:   private void _doHandshake ()
 138:     throws IOException
 139:   {
 140:     // According to the spec, the handshake is always initiated by
 141:     // the debugger, regardless of whether the JVM is in client mode or
 142:     // server mode.
 143: 
 144:     // Wait for handshake from debugger
 145:     byte[] hshake = new byte[_HANDSHAKE.length];
 146:     _inStream.readFully (hshake, 0, _HANDSHAKE.length);
 147: 
 148:     if (Arrays.equals (hshake, _HANDSHAKE))
 149:       {
 150:         // Send reply handshake
 151:         _outStream.write (_HANDSHAKE, 0, _HANDSHAKE.length);
 152:         return;
 153:       }
 154:     else
 155:       {
 156:         throw new IOException ("invalid JDWP handshake (\"" + hshake + "\")");
 157:       }
 158:   }
 159: 
 160:   /**
 161:    * Main run method for the thread. This thread loops waiting for
 162:    * packets to be read via the connection. When a packet is complete
 163:    * and ready for processing, it places the packet in a queue that can
 164:    * be accessed via <code>getPacket</code>
 165:    */
 166:   public void run ()
 167:   {
 168:     // Notify initialization thread (gnu.classpath.jdwp.Jdwp) that
 169:     // the JdwpConnection thread is ready.
 170:     Jdwp.getDefault().subcomponentInitialized ();
 171: 
 172:     while (!_shutdown)
 173:       {
 174:         try
 175:           {
 176:             _readOnePacket ();
 177:           }
 178:         catch (IOException ioe)
 179:           {
 180:             /* IOException can occur for two reasons:
 181:                1. Lost connection with the other side
 182:                2. Transport was shutdown
 183:                In either case, we make sure that all of the
 184:                back-end gets shutdown. */
 185:             Jdwp.getDefault().shutdown ();
 186:           }
 187:         catch (Throwable t)
 188:           {
 189:             System.out.println ("JdwpConnection.run: caught an exception: "
 190:                                 + t);
 191:             // Just keep going
 192:           }
 193:       }
 194:   }
 195: 
 196:   // Reads a single packet from the connection, adding it to the packet
 197:   // queue when a complete packet is ready.
 198:   private void _readOnePacket ()
 199:     throws IOException
 200:   {
 201:     byte[] data = null;
 202: 
 203:     // Read in the packet
 204:     int length = _inStream.readInt ();
 205:     if (length < 11)
 206:       {
 207:         throw new IOException ("JDWP packet length < 11 ("
 208:                                + length + ")");
 209:       }
 210: 
 211:     data = new byte[length];
 212:     data[0] = (byte) (length >>> 24);
 213:     data[1] = (byte) (length >>> 16);
 214:     data[2] = (byte) (length >>> 8);
 215:     data[3] = (byte) length;
 216:     _inStream.readFully (data, 4, length - 4);
 217: 
 218:     JdwpPacket packet = JdwpPacket.fromBytes (data);
 219:     if (packet != null)
 220:       {
 221:         synchronized (_commandQueue)
 222:           {
 223:             _commandQueue.add (packet);
 224:             _commandQueue.notifyAll ();
 225:           }
 226:       }
 227:   }
 228: 
 229:   /**
 230:    * Returns a packet from the queue of ready packets
 231:    *
 232:    * @returns  a <code>JdwpPacket</code> ready for processing
 233:    *           <code>null</code> when shutting down
 234:    */
 235:   public JdwpPacket getPacket ()
 236:   {
 237:     synchronized (_commandQueue)
 238:       {
 239:         while (_commandQueue.isEmpty ())
 240:           {
 241:             try
 242:               {
 243:                 _commandQueue.wait ();
 244:               }
 245:             catch (InterruptedException ie)
 246:               {
 247:                 /* PacketProcessor is interrupted
 248:                    when shutting down */
 249:                 return null;
 250:               }
 251:           }
 252: 
 253:         return (JdwpPacket) _commandQueue.remove (0);
 254:       }
 255:   }
 256: 
 257:   /**
 258:    * Send a packet to the debugger
 259:    *
 260:    * @param pkt a <code>JdwpPacket</code> to send
 261:    * @throws IOException
 262:    */
 263:   public void sendPacket (JdwpPacket pkt)
 264:     throws IOException
 265:   {
 266:     pkt.write (_outStream);
 267:   }
 268: 
 269:   /**
 270:    * Send an event notification to the debugger. Note that this
 271:    * method will only send out one notification: all the events
 272:    * are passed in a single Event.COMPOSITE packet.
 273:    *
 274:    * @param requests  debugger requests for events
 275:    * @param events    the events to send
 276:    * @param suspendPolicy  the suspend policy enforced by the VM
 277:    * @throws IOException
 278:    */
 279:   public void sendEvents(EventRequest[] requests, Event[] events,
 280:                          byte suspendPolicy)
 281:     throws IOException
 282:   {
 283:     JdwpPacket pkt;
 284: 
 285:     synchronized (_bytes)
 286:       {
 287:         _bytes.reset ();
 288:         pkt = Event.toPacket (_doStream, events, requests, suspendPolicy);
 289:         pkt.setData (_bytes.toByteArray ());
 290:       }
 291: 
 292:     sendPacket (pkt);
 293:   }
 294: 
 295:   /**
 296:    * Shutdown the connection
 297:    */
 298:   public void shutdown ()
 299:   {
 300:     if (!_shutdown)
 301:       {
 302:         _transport.shutdown ();
 303:         _shutdown = true;
 304:         interrupt ();
 305:       }
 306:   }
 307: }