PageRenderTime 11ms CodeModel.GetById 4ms app.highlight 27ms RepoModel.GetById 1ms app.codeStats 0ms

/core/com/limegroup/gnutella/io/NIOSocket.java

https://github.com/zootella/learning
Java | 1031 lines | 301 code | 193 blank | 537 comment | 23 complexity | 3314135672756005b9169be1cb6ddffa MD5 | raw file
   1
   2// Commented for the Learning branch
   3
   4package com.limegroup.gnutella.io;
   5
   6import java.io.IOException;
   7import java.io.OutputStream;
   8import java.io.InputStream;
   9import java.nio.channels.SocketChannel;
  10import java.nio.channels.ReadableByteChannel;
  11import java.net.InetAddress;
  12import java.net.Socket;
  13import java.net.SocketTimeoutException;
  14import java.net.UnknownHostException;
  15import java.net.InetSocketAddress;
  16import java.net.SocketException;
  17import java.net.SocketAddress;
  18
  19import org.apache.commons.logging.LogFactory;
  20import org.apache.commons.logging.Log;
  21
  22/**
  23 * A socket that does all of its connecting, reading and writing using NIO.
  24 * 
  25 * This class provides input and output streams you can use for blocking IO.
  26 * The class uses non-blocking IO internally, and makes threads wait to simulate blocking.
  27 * To switch to using event-based reads, call setReadObserver().
  28 * NIO will call the handleRead() method there when it's time to read.
  29 * You have to use a ChannelReadObserver so the socket can set the appropriate underlying channel.
  30 * 
  31 * NIOSocket extends Socket, meaning that it is a socket.
  32 * However, we never use that socket.
  33 * Instead, we create or get passed one and save it in the member variable named socket.
  34 * NIOSocket extends Socket just to use its interface.
  35 * 
  36 * This class is for Java NIO, while socket is a java.net.Socket object.
  37 * This is because NIO didn't define a new kind of socket.
  38 * It just added NIO abilities to java.net.Socket.
  39 * With NIO, you can call Socket s; s.getChannel(); to get an NIO channel from it.
  40 * You can then read and write to that channel without blocking.
  41 * The Socket and SocketChannel objects make up a pair.
  42 * 
  43 * Extends and Implements
  44 * ConnectObserver: NIO can tell this NIOSocket object a connection it initiated is made, handleConnect().
  45 * NIOMultiplexor:  When NIO tells this NIOSocket object to read or write, it has objects to call to do it instead, setReadObserver() and setWriteObserver().
  46 * ReadObserver:    NIO can tell this NIOSocket object to read now, handleRead().
  47 * WriteObserver:   NIO can tell this NIOSocket object to get data and write now, handleWrite().
  48 */
  49public class NIOSocket extends Socket implements ConnectObserver, NIOMultiplexor {
  50
  51    /** Make a log for this class that we can leave notes in to document how the program runs. */
  52    private static final Log LOG = LogFactory.getLog(NIOSocket.class);
  53
  54    /*
  55     * In NIO, a java.net.Socket and its associated java.nio.channels.SocketChannel go together as a pair.
  56     * Given one, you can ask Java for the other.
  57     * NIOSocket keeps both here, and names them socket and channel.
  58     */
  59
  60    /** The java.net.Socket object that gets connected to the remote computer. */
  61    private final Socket socket;
  62    /** The java.nio.channels.SocketChannel object we communicate with the remote computer through. */
  63    private final SocketChannel channel;
  64
  65    /*
  66     * NIOSocket makes 4 LimeWire objects to help exchange data with the remote computer.
  67     * For sending data, it makes a NIOOutputStream and a BufferOutputStream.
  68     * For receiving data, it makes a NIOInputStream and a BufferInputStream.
  69     * 
  70     * NIOOutputStream implements the WriteObserver interface, which requires a handleWrite() method.
  71     * NIO calls handleWrite() to get the NIOOutputStream to send data from its buffer into the channel.
  72     * 
  73     * NIOInputStream implements the ReadObserver interface, which requires a handleRead() method.
  74     * NIO calls handleRead() to get the NIOInputStream to read data from the channel into its buffer.
  75     * 
  76     * To send data to the remote computer, call getOutputStream().
  77     * It will return a reference to the BufferOutputStream object, call write(b) on it.
  78     * 
  79     * To get data the remote computer has sent us, call getInputStream().
  80     * It will return a reference to the BufferInputStream object, call read() on it.
  81     */
  82
  83    /**
  84     * An object that implements the WriteObserver interface.
  85     * This means NIO can call handleWrite() on it when it's time for it to write.
  86     * 
  87     * At first, writer will be the NIOOutputStream object the constructor made.
  88     * Then, ManagedConnection.startOutput() will call setWriteObserver, giving it a new MessageWriter instead.
  89     */
  90    private WriteObserver writer;
  91    
  92    /**
  93     * An object that implements the ReadObserver interface.
  94     * This means NIO can call handleRead() on it when it's time for it to read.
  95     * 
  96     * At first, reader will be the NIOInputStream object the constructor made.
  97     * Then, ManagedConnection.loopForMessages() will call setReadObserver, giving it a new MessageReader instead.
  98     */
  99    private ReadObserver reader;
 100
 101    /** Any exception that occurred while trying to connect */
 102    private IOException storedException = null;
 103
 104    /**
 105     * The IP address of the remote computer our socket and channel are connected to.
 106     * 
 107     * When we disconnect the channel, it won't tell us the IP address we were connected to anymore.
 108     * So, we need to keep a copy of the information here.
 109     */
 110    private InetAddress connectedTo;
 111
 112    /** True if our connection to the remote computer has been shut down. */
 113    private boolean shuttingDown;
 114
 115    /**
 116     * An object NIOSocket uses with wait() and notify() thread calls to make connect() block like LimeWire expects.
 117     * 
 118     * When a thread calls connect(), it waits on this lock for NIO to make the connection.
 119     * When NIO makes the connection, it calls handleConnect(), which wakes the waiting thread up.
 120     * 
 121     * The call to make a thread wait on the lock is LOCK.wait(timeout), this is in connect().
 122     * The call to wake that thread up is LOCK.notify(), this is in handleConnect().
 123     * 
 124     * NIO is non-blocking, but this lock lets it provide a connect() method that blocks.
 125     * A large portion of LimeWire still expects the connect() call to block.
 126     */
 127    private final Object LOCK = new Object();
 128
 129    /**
 130     * Make a new NIOSocket object for a remote computer that has connected to us.
 131     * NIOServerSocket.accept() calls this when a remote computer has connected to the listening socket in the Acceptor class.
 132     * 
 133     * Saves the Socket and SocketChannel pair of objects.
 134     * Makes NIOOutputStream, BufferOutputStream, NIOInputStream, and BufferInputStream objects beneath this one.
 135     * 
 136     * @param s The java.net.Socket connection socket NIO gave us for the remote computer that just connected to us
 137     */
 138    NIOSocket(Socket s) throws IOException {
 139
 140        /*
 141         * In NIO, there are 2 objects that go together for a single connection.
 142         * java.net.Socket is the connection socket.
 143         * java.nio.channels.SocketChannel is the non-blocking communications channel.
 144         * The objects always exist as a pair.
 145         * If you have either one, you can ask Java for a reference to the other.
 146         */
 147
 148        // Save the Socket and SocketChannel objects for this connection in this NIOSocket object
 149        channel = s.getChannel(); // Get the NIOSocketChannel object associated with the given connection java.net.Socket object
 150        socket = s;
 151
 152        // Make NIOInputStream and NIOOutputStream objects that we'll use to exchange data with the remote computer
 153        writer = new NIOOutputStream(this, channel);
 154        reader = new NIOInputStream(this, channel);
 155        ((NIOOutputStream)writer).init(); // Have the NIOOutputStream make a BufferOutputStream and get an 8 KB ByteBuffer
 156        ((NIOInputStream)reader).init();  // Have the NIOInputStream make a BufferInputStream and get an 8 KB ByteBuffer
 157
 158        // Tell NIO we want to know when we can send this remote computer data, and when data has arrived from it for us
 159        NIODispatcher.instance().registerReadWrite(channel, this);
 160        connectedTo = s.getInetAddress();
 161    }
 162
 163    /**
 164     * Make a new NIOSocket object we can use later to try to connect to a remote computer.
 165     * Sockets.connectAndRelease() calls this.
 166     * 
 167     * Makes a new Socket and SocketChannel pair of objects.
 168     * Makes NIOOutputStream and NIOInputStream objects beneath this one.
 169     */
 170    public NIOSocket() throws IOException {
 171
 172        // Make a new pair of Socket and SocketChannel objects
 173        channel = SocketChannel.open(); // Call the static method java.nio.channels.SocketChannel.open() to make a new SocketChannel object
 174        socket = channel.socket();      // Get the java.net.Socket object that it goes with
 175
 176        // Tell NIO we want to use the channel in non-blocking mode
 177        init();
 178
 179        // Make NIOInputStream and NIOOutputStream objects that we'll use to exchange data with the remote computer
 180        writer = new NIOOutputStream(this, channel);
 181        reader = new NIOInputStream(this, channel);
 182    }
 183
 184    /** Only unit test code uses this constructor. */
 185    public NIOSocket(InetAddress addr, int port) throws IOException {
 186
 187        // Only unit test code uses this constructor
 188        channel = SocketChannel.open();
 189        socket = channel.socket();
 190        init();
 191        writer = new NIOOutputStream(this, channel);
 192        reader = new NIOInputStream(this, channel);
 193        connect(new InetSocketAddress(addr, port));
 194    }
 195
 196    /** This constructor is never used. */
 197    public NIOSocket(InetAddress addr, int port, InetAddress localAddr, int localPort) throws IOException {
 198        
 199        // This constructor is never used
 200        channel = SocketChannel.open();
 201        socket = channel.socket();
 202        init();
 203        writer = new NIOOutputStream(this, channel);
 204        reader = new NIOInputStream(this, channel);
 205        bind(new InetSocketAddress(localAddr, localPort));
 206        connect(new InetSocketAddress(addr, port));
 207    }
 208
 209    /** Only unit test code uses this constructor. */
 210    public NIOSocket(String addr, int port) throws UnknownHostException, IOException {
 211
 212        // Only unit test code uses this constructor
 213        this(InetAddress.getByName(addr), port);
 214    }
 215
 216    /** This constructor is never used. */
 217    public NIOSocket(String addr, int port, InetAddress localAddr, int localPort) throws IOException {
 218        
 219        // This constructor is never used
 220        this(InetAddress.getByName(addr), port, localAddr, localPort);
 221    }
 222
 223    /** Configure our SocketChannel channel so it won't block. */
 224    private void init() throws IOException {
 225
 226        // Turn blocking mode for the channel off
 227        channel.configureBlocking(false);
 228    }
 229
 230    /**
 231     * Set this NIOSocket so that when NIO tells it to read, it tells the first object in the chain of readers to read instead.
 232     * The read chain will look like this:
 233     * 
 234     *   (this computer) - MessageReader - InflaterReader - NIOSocket - (remote computer)
 235     * 
 236     * This NIOSocket object at the end contains channel, the SelectableChannel connected to the remote computer.
 237     * NIO will tell channel when there is data for channel to read.
 238     * It will do this by calling NIOSocket.handleRead().
 239     * 
 240     * NIOSocket.handleRead() just calls reader.handleRead(), forwarding the call to the reader this NIOSocket object has saved.
 241     * At the start, this reader is the NIOInputStream that this NIOSocket made for itself.
 242     * 
 243     * Now, MangedConnection has finished building the chain of readers.
 244     * We want NIO's command to read to hit the first object in the chain, MessageReader.
 245     * MessageReader will be told to read, it will read from InflaterReader, and InflaterReader will read from this NIOSocket object.
 246     * 
 247     * This is how a push becomes a pull.
 248     * NIO's notification that the channel can read doesn't push data onto the channel.
 249     * Rather, it causes MessageReader to read, pulling data in through InflaterReader and from us.
 250     * 
 251     * In ManagedConnection, loopForMessages() makes a new MessageReader, the object that slices uncompressed data into Gnutella packets.
 252     * It then calls _socket.setReadObserver(reader), giving us the MessageReader.
 253     * This is the only place this method is called.
 254     * Also, ManagedConnection calls this just one time.
 255     * As LimeWire runs, this method is only used once for each remote computer we're connected to.
 256     * 
 257     * Sets the new ReadObserver.
 258     * Finds the most distant ChannelReader in the chain.
 259     * Sets its source to the prior reader, and tells it to read.
 260     * This empties any data it has buffered.
 261     * With that done, sets the source to the socket's channel.
 262     * Turns interest in reading on.
 263     * 
 264     * @param newReader The MessageReader packet slicer ManagedConnection made.
 265     *                  The object at the start of the read chain.
 266     *                  The object that NIO's command to read should be forwarded to.
 267     */
 268    public void setReadObserver(final ChannelReadObserver newReader) {
 269
 270        /*
 271         * This code defines a new class right here.
 272         * It's not named, but is Runnable, which means it has a run() method.
 273         * A thread can run a Runnable class later.
 274         * We make the class, and hand it to the NIODispatcher object's invokeLater() method.
 275         * When the right thread calls invokeLater(), it calls run() here.
 276         */
 277
 278        // Define a new Runnable class right here, and have the NIODispatcher thread call its run() method later.
 279        NIODispatcher.instance().invokeLater(new Runnable() { public void run() {
 280
 281            // Point oldReader and reader at this NIOSocket object's NIOInputStream
 282            ReadObserver oldReader = reader; // Point oldReader at the NIOInputStream
 283
 284            try {
 285
 286                // Change reader to point at the given MessageReader object instead of the NIOInputStream
 287                reader = newReader;                    // Point reader at the given MessageReader object
 288                ChannelReader lastChannel = newReader; // Point lastChannel at the given MessageReader object
 289
 290                // This loop actually only runs one time
 291                while (lastChannel.getReadChannel() instanceof ChannelReader) {
 292
 293                    // The given MessageReader reads from an InflaterReader, point lastChannel at it
 294                    lastChannel = (ChannelReader)lastChannel.getReadChannel(); // Point lastChannel at the InflaterReader
 295                }
 296
 297                // This is true, oldReader is our NIOInputStream, and newReader is the given MessageReader packet slicer
 298                if (oldReader instanceof ReadableByteChannel && oldReader != newReader) {
 299
 300                    // Empty the data from our NIOInputStream, and shut it down
 301                    lastChannel.setReadChannel((ReadableByteChannel)oldReader); // Set the InflaterReader to read from our NIOInputStream
 302                    reader.handleRead();  // Call MessageReader.handleRead() to have it pull all the buffered data through
 303                    oldReader.shutdown(); // This is the end of the NIOInputStream, call shutdown() on it
 304                }
 305
 306                // Have the InflaterReader read directly from channel, the SocketChannel object connected to the remote computer
 307                lastChannel.setReadChannel(channel);
 308
 309                // Register our channel with NIO so NIO will tell us when our channel has data for us to read
 310                NIODispatcher.instance().interestRead(channel, true);
 311
 312            } catch (IOException iox) {
 313
 314                // Shut down this NIOSocket object, and our NIOInputStream
 315                shutdown();
 316                oldReader.shutdown(); // If the exception happened before we did this above, we'll need to do it here
 317            }
 318        }});
 319    }
 320
 321    /**
 322     * Set this NIOSocket so that when you write to it, you're actually writing to the MessageWriter at the start of the write chain.
 323     * 
 324     * Only ManagedConnection.startOutput() calls this method.
 325     * startOutput() created the base of the chain of writers, which looks like this:
 326     * 
 327     *   MessageWriter -> DeflaterWriter -> DelayedBufferWriter -> ThrottleWriter
 328     * 
 329     * Code does the following things.
 330     * Flush and remove the NIOOutputStream object we were using, but won't be a part of the chain of writers.
 331     * Find the ThrottleWriter at the end of the chain.
 332     * Make a new SocketInterestWriteAdapater.
 333     * 
 334     * The SocketInterestWriteAdapter is core to how LimeWire writes.
 335     * When you call handleWrite() on this NIOSocket object, the call will go to SocketInterestWriteAdapter.handleWrite().
 336     * When ThrottleWriter writes, it sends data to the SocketInterestWriteAdapter.
 337     * The SocketInterestWriteAdapter writes to channel, the actual java.nio.channels.SocketChannel object connected to the remote computer.
 338     * 
 339     * We'll use the SocketInterestWriteAdapter as a hub to receive and forward interest calls.
 340     * 
 341     * @param newWriter The MessageWriter ManagedConnection made that starts the base of the chain of writers
 342     */
 343    public void setWriteObserver(final ChannelWriter newWriter) {
 344
 345        // Define a new Runnable class right here, and have the NIODispatcher call its run() method from a different thread later.
 346        NIODispatcher.instance().invokeLater(new Runnable() { public void run() {
 347
 348            try {
 349                
 350                /*
 351                 * Before we change it, writer points at the NIOOutputStream object this NIOSocket made.
 352                 * Call handleWrite() on it to make it send out all its data.
 353                 * If it wasn't able to send everything out, it will return true.
 354                 * This is a problem, there is still data stuck in the NIOOutputStream, and we're about to replace it with the given MessageWriter.
 355                 * If this happens, throw an exception.
 356                 */
 357
 358                // Tell our NIOInputStream to send all its data to the remote computer, and make sure it has none left in it
 359                if (writer.handleWrite()) throw new IllegalStateException("data still in old writer!");
 360                writer.shutdown(); // Shut down our NIOInputStream, we're done with it
 361
 362                // Find the ThrottleWriter at the end of the chain
 363                ChannelWriter lastChannel = newWriter; // Start lastChannel at the starting MessageWriter
 364                while (lastChannel.getWriteChannel() instanceof ChannelWriter) {
 365
 366                    // Find the object that lastChannel writes to, and point lastChannel at it instead
 367                    lastChannel = (ChannelWriter)lastChannel.getWriteChannel();
 368
 369                    /*
 370                     * This loop runs 3 times.
 371                     * At this point, lastChannel points to: (1) DeflaterWriter, (2) DelayedBufferWriter, (3) ThrottleWriter
 372                     * The last one ThrottleWriter, implements ThrottleListener, so control enters the if statement.
 373                     * The loop then exits, and lastChannel points to ThrottleWriter.
 374                     */
 375
 376                    // lastChannel points to ThrottleWriter
 377                    if (lastChannel instanceof ThrottleListener) {
 378
 379                        // Call ThrottleListener.setAttachment(), giving it a reference to this class
 380                        ((ThrottleListener)lastChannel).setAttachment(NIOSocket.this); // (ask)
 381                    }
 382                }
 383
 384                // Make a new SocketInterestWriteAdapter
 385                InterestWriteChannel source = new SocketInterestWriteAdapater(channel); // It's the object that will actually write to the Java SelectableChannel
 386                writer = source;                     // When you write to this NIOSocket object, you'll write to it
 387                lastChannel.setWriteChannel(source); // When the ThrottleWriter writes, it will write to it also
 388
 389            } catch (IOException iox) {
 390
 391                // Shut down this NIOSocket object, and our NIOOutputStream
 392                shutdown();
 393                newWriter.shutdown(); // If the exception happened before we did this above, we'll need to do it here
 394            }
 395        }});
 396    }
 397
 398    /**
 399     * NIO calls this when our channel is connected.
 400     * 
 401     * There is a thread waiting in the connect() method right now.
 402     * It told NIO to connect the channel, and then called LOCK.wait().
 403     * It's blocking in connect() until the connection is made.
 404     * 
 405     * The connection is made now.
 406     * Call LOCK.notify() to wake the thread in connect() up.
 407     * It will return to its caller, having blocked for as long as it took to make the connection.
 408     */
 409    public void handleConnect() throws IOException {
 410
 411        // Make sure only one thread can do this at a time
 412        synchronized (LOCK) {
 413
 414            // Wake up the thread in connect() that called LOCK.wait()
 415            LOCK.notify();
 416        }
 417    }
 418
 419    /**
 420     * NIODispatcher calls this when we can read from our channel.
 421     * Passes the call to the NIOInputStream object this NIOSocket made.
 422     * After ManagedConnection has created the chain of readers, passes the call to the MessageReader at the start of the chain.
 423     */
 424    public void handleRead() throws IOException {
 425
 426        // Calls NIOInputStream.handleRead() or MessageReader.handleRead()
 427        reader.handleRead();
 428    }
 429
 430    /**
 431     * NIODispatcher calls this when we can write to our channel.
 432     * Passes the call to the NIOOutputStream object this NIOSocket made.
 433     * After ManagedConnection has created the chain of writers, passes the call to the MessageWriter at the start of the chain.
 434     */
 435    public boolean handleWrite() throws IOException {
 436
 437        // Calls NIOOutputStream.handleWrite() or MessageWriter.handleWrite()
 438        return writer.handleWrite();
 439    }
 440
 441    /**
 442     * NIO calls this when an IOException occurred while it was processing a connect, read, or write. (do)
 443     * Shuts down the socket and all of its streams.
 444     * Wakes up any threads waiting on locks.
 445     * 
 446     * @param iox The IOException that happened
 447     */
 448    public void handleIOException(IOException iox) {
 449
 450        // Make sure only one thread can do this at a time
 451        synchronized (LOCK) {
 452
 453            /*
 454             * Save the given exception in storedException.
 455             * Call LOCK.notify() to wake up the thread sleeping in the connect() method.
 456             * It will check out storedException.
 457             * If there is an exception there, it will call shtudown(), and then throw it.
 458             */
 459
 460            // Save the given exception in storedException, and wake up the thread in connect() so it will get it
 461            storedException = iox;
 462            LOCK.notify();
 463        }
 464
 465        // Shut this NIOSocket object down
 466        shutdown(); // The shuttingDown boolean will keep the method from running twice
 467    }
 468
 469    /**
 470     * Shut down this socket and all of its streams.
 471     * Wake up threads waiting on locks.
 472     * 
 473     * Closing things and shutting them down can cause IOExceptions.
 474     * Code here catches them, and does nothing, ignoring them.
 475     * There's nothing we can do about internal errors.
 476     * And, we're shutting down anyway, so it doesn't matter if something breaks.
 477     * 
 478     * The Shutdownable interface requires this method.
 479     */
 480    public void shutdown() {
 481
 482        // Only let one thread in this section at a time
 483        synchronized (LOCK) {
 484
 485            // Make sure this shutdown() method only runs once
 486            if (shuttingDown) return; // If it's already run, leave now
 487            shuttingDown = true;      // Mark shuttingDown true so we won't get past here again
 488        }
 489
 490        // If the Apache log is on, make a note that we're shutting down
 491        if (LOG.isDebugEnabled()) LOG.debug("Shutting down socket & streams for: " + this);
 492        
 493        /*
 494         * socket is a java.net.Socket object that we're using with NIO.
 495         * The method calls below call socket.shutdownInput() and socket.shutdownOutput().
 496         * This tells Java we won't be reading or writing from the socket anymore.
 497         * It also ends our ability to do these things.
 498         */
 499
 500        // Shut down this object's socket for reading and writing
 501        try { shutdownInput();  } catch (IOException ignored) {} // Calls socket.shutdownInput();
 502        try { shutdownOutput(); } catch (IOException ignored) {} // Calls socket.shutdownOutput();
 503        
 504        // Tell our NIOInputStream and NIOOutputStream objects to shut down
 505        reader.shutdown();
 506        writer.shutdown();
 507
 508        try {
 509
 510            // Close the java.net.Socket object
 511            socket.close();
 512
 513        // Ignore exceptions
 514        } catch (IOException ignored) {
 515        } catch (Error ignored) {
 516        }
 517        
 518        // Close the NIO SocketChannel we got from our socket
 519        try { channel.close(); } catch (IOException ignored) {}
 520
 521        // Only let one thread in this section at a time
 522        synchronized (LOCK) {
 523
 524            /*
 525             * Another thread may have called LOCK.wait();
 526             * That made it block right there.
 527             * When we call LOCK.notify(), that thread will keep going.
 528             */
 529
 530            // Wake up a thread waiting on the lock object
 531            LOCK.notify();
 532        }
 533    }
 534
 535    /** Not used. */
 536    public void bind(SocketAddress endpoint) throws IOException {
 537
 538        // Have the socket do this
 539        socket.bind(endpoint);
 540    }
 541
 542    /**
 543     * Shut down this socket and all of its streams.
 544     * Wake up threads waiting on locks.
 545     */
 546    public void close() throws IOException {
 547
 548        // This just calls the shutdown() method here in NIOSocket
 549        NIODispatcher.instance().shutdown(this);
 550    }
 551
 552    /** Only used in test code. */
 553    public void connect(SocketAddress addr) throws IOException {
 554
 555        // Connect with no timeout, wait forever if necessary
 556        connect(addr, 0);
 557    }
 558
 559    /**
 560     * Have NIO connect the SocketChannel to the given address, and wait here until it makes the connection.
 561     * 
 562     * This method uses Java NIO to connect to the remote computer.
 563     * NIO doesn't block, the call channel.connect(addr) below returns immediately, and you can find out later if it worked.
 564     * But, the part of LimeWire above this method is built to have a thread wait to connect and then wait as we exchange handshake headers.
 565     * So, this method fakes blocking for the benefit of the rest of the program.
 566     * It takes a new, robust non-blocking NIO system and converts it to an old-fashioned, blocking one.
 567     * 
 568     * A thread is meant to call connect(), get stuck here, and leave once we're connected or a timeout has expired.
 569     * Later, when we start exchanging packets with the remote computer through this connection, we'll use NIO without this fake blocking.
 570     * 
 571     * @param addr    The IP address and port number to try to connect to
 572     * @param timeout After telling NIO to connect, the thread will wait here for up to 6 seconds
 573     */
 574    public void connect(SocketAddress addr, int timeout) throws IOException {
 575
 576        /*
 577         * Here are 3 Java types that hold IP address and port number information.
 578         * SocketAddress and InetSocketAddress are the same.
 579         * They hold the IP address and port number together.
 580         * InetAddress just holds the IP address.
 581         */
 582
 583        // Keep a record of the IP address and port number we connected to
 584        connectedTo = ((InetSocketAddress)addr).getAddress();
 585        
 586        // Make sur the given InetSocketAddress contains an IP address
 587        InetSocketAddress iaddr = (InetSocketAddress)addr;
 588        if (iaddr.isUnresolved()) throw new IOException("unresolved: " + addr);
 589
 590        // Only let one thread in this section at a time
 591        synchronized (LOCK) {
 592
 593            /*
 594             * This is where LimeWire actually asks Java to connect to an IP address and port number.
 595             * 
 596             * Here's how to connect to a remote computer the NIO way.
 597             * Make a Socket, and then call getChannel() on it to get its associated SocketChannel object.
 598             * Here, it's named channel.
 599             * Package the IP address and port number you want to connect to in an InetSocketAddress object.
 600             * Here, it's named addr.
 601             * Call channel.connect(addr).
 602             * 
 603             * If Java can establish the connection immediately, the connect(addr) method returns true.
 604             * This usually happens on local connections.
 605             * If it's going to take a little while, connect(addr) returns false.
 606             * 
 607             * This call doesn't block, and returns right away.
 608             * NIO will tell you later if it was able to make the connection.
 609             */
 610
 611            // Have Java try to open a connection to the given IP address and port number
 612            if (!channel.connect(addr)) {
 613
 614                /*
 615                 * channel.connect(addr) returned false.
 616                 * This means it's going to take a little while for NIO to contact the remote computer.
 617                 */
 618
 619                // Tell NIO that we are interested in knowing when this channel has been connected
 620                NIODispatcher.instance().registerConnect(
 621                    channel, // The channel that NIO is connecting to the remote computer
 622                    this);   // NIO will call this object's handleConnect() method when the connection is made
 623
 624                try {
 625
 626                    /*
 627                     * Have this thread wait here for up to 6 seconds.
 628                     * When NIO calls handleConnect() above, the thread running that method will call LOCK.notify().
 629                     * When it does that, it wakes up the thread here.
 630                     */
 631
 632                    // Wait here until NIO calls handleConnect(), meaning the connection is made
 633                    LOCK.wait(timeout); // Or 6 seconds pass
 634
 635                // If another thread called LOCK.interrupt(), the wait() method above will throw an InterruptedException
 636                } catch (InterruptedException ix) {
 637
 638                    // Wrap it in an InterruptedIOException and throw it upward
 639                    throw new InterruptedIOException(ix);
 640                }
 641
 642                /*
 643                 * NIO may have called the handleIOException() method above.
 644                 * If it did, that method caught the exception it threw us and stored it in storedException.
 645                 */
 646
 647                // Move the exception from storedException to x
 648                IOException x = storedException;
 649                storedException = null;
 650
 651                // If NIO did throw us an exception
 652                if (x != null) {
 653
 654                    // Shut down this NIOSocket object and all of the objects it made to use, and throw the exception upwards
 655                    shutdown();
 656                    throw x;
 657                }
 658
 659                // If the socket isn't connected
 660                if (!isConnected()) { // wait above must have timed out after 6 seconds instead of getting woken up by handleConnect()
 661
 662                    // Shut down this NIOSocket object and all of the objects it made to use, and throw an exception that explains how long we waited
 663                    shutdown();
 664                    throw new SocketTimeoutException("couldn't connect in " + timeout + " milliseconds");
 665                }
 666            }
 667
 668            /*
 669             * If channel.connect(addr) makes the connection immediately, it returns true.
 670             * Control reaches here without running the inside of the if block at all.
 671             */
 672        }
 673
 674        // Make a note about the connection we just made in the Apache debugging log
 675        if (LOG.isTraceEnabled()) LOG.trace("Connected to: " + addr);
 676
 677        // This NIOSocket object made NIOOutputStream and NIOInputStream objects, call init() on both of them
 678        if (writer instanceof NIOOutputStream) ((NIOOutputStream)writer).init(); // Have NIOOutputStream make a BufferOutputStream and get an 8 KB buffer
 679        if (reader instanceof NIOInputStream)  ((NIOInputStream)reader).init();  // Have NIOInputStream make a BufferInputStream and get an 8 KB buffer
 680    }
 681
 682    /**
 683     * The IP address of the remote computer this socket is connected to.
 684     * 
 685     * If the remote computer connected to us, we got the IP address from socket.getInetAddress().
 686     * If we connected to the remote computer, we saved the given IP address before we tried to connect to it.
 687     */
 688    public InetAddress getInetAddress() {
 689
 690        // Return the value we saved
 691        return connectedTo;
 692    }
 693
 694    /**
 695     * Get the object this NIOSocket made that implements InputStream and you can read from.
 696     * Calls to read will block, even though the NIO underneath is non-blocking.
 697     * 
 698     * @return The BufferInputStream the NIOInputSteam made, which you can call read() on
 699     */
 700    public InputStream getInputStream() throws IOException {
 701
 702        // Make sure the socket is open
 703        if (isClosed()) throw new IOException("Socket closed.");
 704
 705        // Return a reference to the BufferInputStream the NIOInputStream made
 706        if (reader instanceof NIOInputStream) return ((NIOInputStream)reader).getInputStream();
 707        else throw new IllegalStateException("reader not NIOInputStream!");
 708    }
 709
 710    /**
 711     * Get the object this NIOSocket made that implements OutputStream and you can write to.
 712     * Calls to write will block, even though the NIO underneath is non-blocking.
 713     * 
 714     * @return The BufferOutputStream the NIOOutputStream made, which you can call write(b) on
 715     */
 716    public OutputStream getOutputStream() throws IOException {
 717
 718        // Make sure the socket is open
 719        if (isClosed()) throw new IOException("Socket closed.");
 720
 721        // Return a reference to the BufferOutputStream the NIOOutputStream made
 722        if (writer instanceof NIOOutputStream) return ((NIOOutputStream)writer).getOutputStream();
 723        else throw new IllegalStateException("writer not NIOOutputStream!");
 724    }
 725
 726    /*
 727     * LimeWire's NIOSocket class extends Socket, so it is a socket.
 728     * It also has a member object of type Socket named socket, so it contains a socket.
 729     * So there are 2 different sockets here.
 730     * 
 731     * NIOSocket never uses the socket that it is, and only uses the socket that it contains instead.
 732     * This class extends Socket just to steal its interface.
 733     * It overrides methods Socket defines and passes them on to the member socket object.
 734     */
 735
 736    /**
 737     * Return the NIO SocketChannel object associated with the Socket this NIOSocket object keeps.
 738     */
 739    public SocketChannel getChannel() {
 740
 741        // Have the socket do this
 742        return socket.getChannel();
 743    }
 744
 745    /**
 746     * Only test code calls this.
 747     * Get the local port number the Socket this NIOSocket object keeps is bound to.
 748     */
 749    public int getLocalPort() {
 750
 751        // Have the socket do this
 752        return socket.getLocalPort();
 753    }
 754
 755    /** Not used. */
 756    public SocketAddress getLocalSocketAddress() {
 757
 758        // Have the socket do this
 759        return socket.getLocalSocketAddress();
 760    }
 761
 762    /**
 763     * Use the connection socket in this NIOSocket object to find out what our IP address is.
 764     * Asks the socket for the local address, the one on the near end.
 765     * This is still just our LAN address, but it's a little better than calling InetAddress.getLocalHost().
 766     * 
 767     * @return Our IP address as an InetAddress object
 768     */
 769    public InetAddress getLocalAddress() {
 770
 771        try {
 772
 773            // Have the socket do this
 774            return socket.getLocalAddress();
 775
 776        } catch (Error osxSucks) {
 777
 778            /*
 779             * On Macintosh OS X 10.3 with Java 1.4.2_05,
 780             * if the connection dies and you call socket.getLocalAddress(),
 781             * it will throw an exception.
 782             */
 783
 784            try {
 785
 786                // Ask Java what our IP address is, and return that instead
 787                return InetAddress.getLocalHost(); // Returns our LAN IP address, like 192.168.1.102
 788
 789            // InetAddress.getLocalHost can sometimes throw an exception, catch it and return null instead
 790            } catch (UnknownHostException uhe) { return null; }
 791        }
 792    }
 793
 794    /** Not used. */ 
 795    public boolean getOOBInline() throws SocketException {
 796
 797        // Have the socket do this
 798        return socket.getOOBInline();
 799    }
 800
 801    /**
 802     * The remote computer's port number.
 803     * Calls socket.getPort() to ask the connection socket what port number on on its far end.
 804     * 
 805     * @return The port number the socket is connected to
 806     */
 807    public int getPort() {
 808
 809        // Ask the socket this
 810        return socket.getPort();
 811    }
 812
 813    /** Not used. */
 814    public int getReceiveBufferSize() throws SocketException {
 815
 816        // Ask the socket this
 817        return socket.getReceiveBufferSize();
 818    }
 819
 820    /** Not used. */
 821    public boolean getReuseAddress() throws SocketException {
 822
 823        // Ask the socket this
 824        return socket.getReuseAddress();
 825    }
 826
 827    /** Not used. */
 828    public int getSendBufferSize() throws SocketException {
 829
 830        // Ask the socket this
 831        return socket.getSendBufferSize();
 832    }
 833
 834    /** Not used. */
 835    public int getSoLinger() throws SocketException {
 836
 837        // Ask the socket this
 838        return socket.getSoLinger();
 839    }
 840
 841    /**
 842     * Find out how long the socket will wait for something to happen before giving up.
 843     * 
 844     * @param The socket timeout, or 0 if the socket will wait forever
 845     */
 846    public int getSoTimeout() throws SocketException {
 847
 848        // Ask the socket this
 849        return socket.getSoTimeout();
 850    }
 851
 852    /** Not used. */
 853    public boolean getTcpNoDelay() throws SocketException {
 854
 855        // Ask the socket this
 856        return socket.getTcpNoDelay();
 857    }
 858
 859    /** Not used. */
 860    public int getTrafficClass() throws SocketException {
 861
 862        // Ask the socket this
 863        return socket.getTrafficClass();
 864    }
 865
 866    /** Not used. */
 867    public boolean isBound() {
 868
 869        // Ask the socket this
 870        return socket.isBound();
 871    }
 872
 873    /**
 874     * Ask the connection socket in this NIOSocket object if it's closed or not.
 875     * 
 876     * @return True if the socket is closed
 877     */
 878    public boolean isClosed() {
 879
 880        // Ask the socket this
 881        return socket.isClosed();
 882    }
 883
 884    /**
 885     * Ask the connection socket in this NIOSocket object if it's connected to a remote computer or not.
 886     * 
 887     * @return True if the socket is connected.
 888     */
 889    public boolean isConnected() {
 890
 891        // Ask the socket this
 892        return socket.isConnected();
 893    }
 894
 895    /** Not used. */
 896    public boolean isInputShutdown() {
 897
 898        // Ask the socket this
 899        return socket.isInputShutdown();
 900    }
 901
 902    /** Not used. */
 903    public boolean isOutputShutdown() {
 904
 905        // Ask the socket this
 906        return socket.isOutputShutdown();
 907    }
 908
 909    /** Not used. */
 910    public void sendUrgentData(int data) {
 911
 912        // Instead of having the socket do this, throw an exception
 913        throw new UnsupportedOperationException("No urgent data.");
 914    }
 915
 916    /**
 917     * Enable or disable SO_KEEPALIVE on the connection socket this NIOSocket object keeps.
 918     * 
 919     * @param b True to enable the SO_KEEPALIVE socket option, false to disable it
 920     */
 921    public void setKeepAlive(boolean b) throws SocketException {
 922
 923        // Have the socket do this
 924        socket.setKeepAlive(b);
 925    }
 926
 927    /** Not used. */
 928    public void setOOBInline(boolean b) throws SocketException {
 929
 930        // Have the socket do this
 931        socket.setOOBInline(b);
 932    }
 933
 934    /** Not used. */
 935    public void setReceiveBufferSize(int size) throws SocketException {
 936
 937        // Have the socket do this
 938        socket.setReceiveBufferSize(size);
 939    }
 940
 941    /** Not used. */
 942    public void setReuseAddress(boolean on) throws SocketException {
 943
 944        // Have the socket do this
 945        socket.setReuseAddress(on);
 946    }
 947
 948    /** Not used. */
 949    public void setSendBufferSize(int size) throws SocketException {
 950
 951        // Set the socket this way
 952        socket.setSendBufferSize(size);
 953    }
 954
 955    /** Not used. */
 956    public void setSoLinger(boolean on, int linger) throws SocketException {
 957
 958        // Set the socket this way
 959        socket.setSoLinger(on, linger);
 960    }
 961
 962    /**
 963     * Set the amount of time the socket in this NIOSocket object should wait before giving up.
 964     * 
 965     * @param timeout The timeout, in milliseconds
 966     */
 967    public void setSoTimeout(int timeout) throws SocketException {
 968
 969        // Set the socket this way
 970        socket.setSoTimeout(timeout);
 971    }
 972
 973    /** Not used. */
 974    public void setTcpNoDelay(boolean b) throws SocketException {
 975        
 976        // Set the socket this way
 977        socket.setTcpNoDelay(b);
 978    }
 979
 980    /** Not used. */
 981    public void setTrafficClass(int i) throws SocketException {
 982        
 983        // Set the socket this way
 984        socket.setTrafficClass(i);
 985    }
 986
 987    /**
 988     * Shut down the socket so we can't read from it anymore.
 989     * 
 990     * socket is a java.net.Socket object that we're using with NIO.
 991     * This method calls the shutdownInput() method on our Socket object.
 992     * 
 993     * Places the input stream for this socket at the end of the stream.
 994     * Any data sent to the input stream side of the socket is acknowledged and then silently discarded.
 995     * If you read from a socket input stream after invoking shutdownInput() on the socket, the stream will return EOF. 
 996     */
 997    public void shutdownInput() throws IOException {
 998
 999        // Shut down the socket for reading
1000        socket.shutdownInput();
1001    }
1002
1003    /**
1004     * Shut down the socket so we can't write to it anymore.
1005     * 
1006     * socket is a java.net.Socket object that we're using with NIO.
1007     * This method calls the shutdownOutput() method on our Socket object.
1008     * 
1009     * Disables the output stream for this socket.
1010     * 
1011     * This is a TCP socket.
1012     * Java will actually send the data we've previously written to the socket, and then do the TCP connection termination sequence.
1013     * If you write to a socket output stream after invoking shutdownOutput() on the socket, the stream will throw an IOException.
1014     */
1015    public void shutdownOutput() throws IOException {
1016
1017        // Shut down the socket for writing
1018        socket.shutdownOutput();
1019    }
1020
1021    /**
1022     * Get information about this NIOSocket object as text in a string.
1023     * 
1024     * @return Text like (do)
1025     */
1026    public String toString() {
1027
1028        // Compose text like (do)
1029        return "NIOSocket::" + channel.toString();
1030    }
1031}