    View Javadoc
    1   package org.apache.commons.net.telnet;
    2
    3   /* ====================================================================
    4    * The Apache Software License, Version 1.1
    5    *
    6    * Copyright (c) 2001 The Apache Software Foundation.  All rights
    7    * reserved.
    8    *
    9    * Redistribution and use in source and binary forms, with or without
    10   * modification, are permitted provided that the following conditions
    11   * are met:
    12   *
    13   * 1. Redistributions of source code must retain the above copyright
    14   *    notice, this list of conditions and the following disclaimer.
    15   *
    16   * 2. Redistributions in binary form must reproduce the above copyright
    17   *    notice, this list of conditions and the following disclaimer in
    18   *    the documentation and/or other materials provided with the
    19   *    distribution.
    20   *
    21   * 3. The end-user documentation included with the redistribution,
    22   *    if any, must include the following acknowledgment:
    23   *       "This product includes software developed by the
    24   *        Apache Software Foundation (http://www.apache.org/)."
    25   *    Alternately, this acknowledgment may appear in the software itself,
    26   *    if and wherever such third-party acknowledgments normally appear.
    27   *
    28   * 4. The names "Apache" and "Apache Software Foundation" and
    29   *    "Apache Commons" must not be used to endorse or promote products
    30   *    derived from this software without prior written permission. For
    31   *    written permission, please contact apache@apache.org.
    32   *
    33   * 5. Products derived from this software may not be called "Apache",
    34   *    nor may "Apache" appear in their name, without
    35   *    prior written permission of the Apache Software Foundation.
    36   *
    37   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
    38   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
    39   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
    40   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
    41   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    42   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
    43   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
    44   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
    45   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
    46   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
    47   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
    48   * SUCH DAMAGE.
    49   * ====================================================================
    50   *
    51   * This software consists of voluntary contributions made by many
    52   * individuals on behalf of the Apache Software Foundation.  For more
    53   * information on the Apache Software Foundation, please see
    54   * <http://www.apache.org/>;.
    55   */
    56
    57  import java.io.BufferedInputStream;
    58  import java.io.IOException;
    59  import java.io.InputStream;
    60  import java.io.InterruptedIOException;
    61
    62  /****
    63   *
    64   * <p>
    65   *
    66   * <p>
    67   * <p>
    68   * @author Daniel F. Savarese
    69   ***/
    70
    71
    72  final class TelnetInputStream extends BufferedInputStream implements Runnable
    73  {
    74      static final int _STATE_DATA = 0, _STATE_IAC = 1, _STATE_WILL = 2,
    75                       _STATE_WONT = 3, _STATE_DO = 4, _STATE_DONT = 5,
    76                       _STATE_SB = 6, _STATE_SE = 7, _STATE_CR = 8;
    77
    78      private boolean __hasReachedEOF, __isClosed;
    79      private boolean __readIsWaiting;
    80      private int __receiveState, __queueHead, __queueTail, __bytesAvailable;
    81      private int[] __queue;
    82      private TelnetClient __client;
    83      private Thread __thread;
    84      private IOException __ioException;
    85
    86      TelnetInputStream(InputStream input, TelnetClient client)
    87      {
    88          super(input);
    89          __client = client;
    90          __receiveState = _STATE_DATA;
    91          __isClosed = true;
    92          __hasReachedEOF = false;
    93          // Make it 1025, because when full, one slot will go unused, and we
    94          // want a 1024 byte buffer just to have a round number (base 2 that is)
    95          //__queue         = new int[1025];
    96          __queue = new int[2049];
    97          __queueHead = 0;
    98          __queueTail = 0;
    99          __bytesAvailable = 0;
    100         __ioException = null;
    101         __readIsWaiting = false;
    102         __thread = new Thread(this);
    103     }
    104
    105     void _start()
    106     {
    107         int priority;
    108         __isClosed = false;
    109         // Need to set a higher priority in case JVM does not use pre-emptive
    110         // threads.  This should prevent scheduler induced deadlock (rather than
    111         // deadlock caused by a bug in this code).
    112         priority = Thread.currentThread().getPriority() + 1;
    113         if (priority > Thread.MAX_PRIORITY)
    114             priority = Thread.MAX_PRIORITY;
    115         __thread.setPriority(priority);
    116         __thread.setDaemon(true);
    117         __thread.start();
    118     }
    119
    120
    121     // synchronized(__client) critical sections are to protect against
    122     // TelnetOutputStream writing through the telnet client at same time
    123     // as a processDo/Will/etc. command invoked from TelnetInputStream
    124     // tries to write.
    125     private int __read() throws IOException
    126     {
    127         int ch;
    128
    129 _loop:
    130         while (true)
    131         {
    132             // Exit only when we reach end of stream.
    133             if ((ch = super.read()) < 0)
    134                 return -1;
    135
    136             ch = (ch & 0xff);
    137
    138 _mainSwitch:
    139             switch (__receiveState)
    140             {
    141
    142             case _STATE_CR:
    143                 if (ch == '\0')
    144                 {
    145                     // Strip null
    146                     continue;
    147                 }
    148                 // How do we handle newline after cr?
    149                 //  else if (ch == '\n' && _requestedDont(TelnetOption.ECHO) &&
    150
    151                 // Handle as normal data by falling through to _STATE_DATA case
    152
    153             case _STATE_DATA:
    154                 if (ch == TelnetCommand.IAC)
    155                 {
    156                     __receiveState = _STATE_IAC;
    157                     continue;
    158                 }
    159
    160
    161                 if (ch == '\r')
    162                 {
    163                     synchronized (__client)
    164                     {
    165                         if (__client._requestedDont(TelnetOption.BINARY))
    166                             __receiveState = _STATE_CR;
    167                         else
    168                             __receiveState = _STATE_DATA;
    169                     }
    170                 }
    171                 else
    172                     __receiveState = _STATE_DATA;
    173                 break;
    174
    175             case _STATE_IAC:
    176                 switch (ch)
    177                 {
    178                 case TelnetCommand.WILL:
    179                     __receiveState = _STATE_WILL;
    180                     continue;
    181                 case TelnetCommand.WONT:
    182                     __receiveState = _STATE_WONT;
    183                     continue;
    184                 case TelnetCommand.DO:
    185                     __receiveState = _STATE_DO;
    186                     continue;
    187                 case TelnetCommand.DONT:
    188                     __receiveState = _STATE_DONT;
    189                     continue;
    190                 case TelnetCommand.IAC:
    191                     __receiveState = _STATE_DATA;
    192                     break;
    193                 default:
    194                     break;
    195                 }
    196                 __receiveState = _STATE_DATA;
    197                 continue;
    198             case _STATE_WILL:
    199                 synchronized (__client)
    200                 {
    201                     __client._processWill(ch);
    202                     __client._flushOutputStream();
    203                 }
    204                 __receiveState = _STATE_DATA;
    205                 continue;
    206             case _STATE_WONT:
    207                 synchronized (__client)
    208                 {
    209                     __client._processWont(ch);
    210                     __client._flushOutputStream();
    211                 }
    212                 __receiveState = _STATE_DATA;
    213                 continue;
    214             case _STATE_DO:
    215                 synchronized (__client)
    216                 {
    217                     __client._processDo(ch);
    218                     __client._flushOutputStream();
    219                 }
    220                 __receiveState = _STATE_DATA;
    221                 continue;
    222             case _STATE_DONT:
    223                 synchronized (__client)
    224                 {
    225                     __client._processDont(ch);
    226                     __client._flushOutputStream();
    227                 }
    228                 __receiveState = _STATE_DATA;
    229                 continue;
    230             }
    231
    232             break;
    233         }
    234
    235         return ch;
    236     }
    237
    238
    239
    240     public int read() throws IOException
    241     {
    242         // Critical section because we're altering __bytesAvailable,
    243         // __queueHead, and the contents of _queue in addition to
    244         // testing value of __hasReachedEOF.
    245         synchronized (__queue)
    246         {
    247
    248             while (true)
    249             {
    250                 if (__ioException != null)
    251                 {
    252                     IOException e;
    253                     e = __ioException;
    254                     __ioException = null;
    255                     throw e;
    256                 }
    257
    258                 if (__bytesAvailable == 0)
    259                 {
    260                     // Return -1 if at end of file
    261                     if (__hasReachedEOF)
    262                         return -1;
    263
    264                     // Otherwise, we have to wait for queue to get something
    265                     __queue.notify();
    266                     try
    267                     {
    268                         __readIsWaiting = true;
    269                         __queue.wait();
    270                         __readIsWaiting = false;
    271                     }
    272                     catch (InterruptedException e)
    273                     {
    274                         throw new IOException("Fatal thread interruption during read.");
    275                     }
    276                     continue;
    277                 }
    278                 else
    279                 {
    280                     int ch;
    281
    282                     ch = __queue[__queueHead];
    283
    284                     if (++__queueHead >= __queue.length)
    285                         __queueHead = 0;
    286
    287                     --__bytesAvailable;
    288
    289                     return ch;
    290                 }
    291             }
    292         }
    293     }
    294
    295
    296     /****
    297      * Reads the next number of bytes from the stream into an array and
    298      * returns the number of bytes read.  Returns -1 if the end of the
    299      * stream has been reached.
    300      * <p>
    301      * @param buffer  The byte array in which to store the data.
    302      * @return The number of bytes read. Returns -1 if the
    303      *          end of the message has been reached.
    304      * @exception IOException If an error occurs in reading the underlying
    305      *            stream.
    306      ***/
    307     public int read(byte buffer[]) throws IOException
    308     {
    309         return read(buffer, 0, buffer.length);
    310     }
    311
    312
    313     /****
    314      * Reads the next number of bytes from the stream into an array and returns
    315      * the number of bytes read.  Returns -1 if the end of the
    316      * message has been reached.  The characters are stored in the array
    317      * starting from the given offset and up to the length specified.
    318      * <p>
    319      * @param buffer The byte array in which to store the data.
    320      * @param offset  The offset into the array at which to start storing data.
    321      * @param length   The number of bytes to read.
    322      * @return The number of bytes read. Returns -1 if the
    323      *          end of the stream has been reached.
    324      * @exception IOException If an error occurs while reading the underlying
    325      *            stream.
    326      ***/
    327     public int read(byte buffer[], int offset, int length) throws IOException
    328     {
    329         int ch, off;
    330
    331         if (length < 1)
    332             return 0;
    333
    334         // Critical section because run() may change __bytesAvailable
    335         synchronized (__queue)
    336         {
    337             if (length > __bytesAvailable)
    338                 length = __bytesAvailable;
    339         }
    340
    341         if ((ch = read()) == -1)
    342             return -1;
    343
    344         off = offset;
    345
    346         do
    347         {
    348             buffer[offset++] = (byte)ch;
    349         }
    350         while (--length > 0 && (ch = read()) != -1);
    351
    352         return (offset - off);
    353     }
    354
    355
    356     /**** Returns false.  Mark is not supported. ***/
    357     public boolean markSupported()
    358     {
    359         return false;
    360     }
    361
    362     public int available() throws IOException
    363     {
    364         // Critical section because run() may change __bytesAvailable
    365         synchronized (__queue)
    366         {
    367             return __bytesAvailable;
    368         }
    369     }
    370
    371
    372     // Cannot be synchronized.  Will cause deadlock if run() is blocked
    373     // in read because BufferedInputStream read() is synchronized.
    374     public void close() throws IOException
    375     {
    376         // Completely disregard the fact thread may still be running.
    377         // We can't afford to block on this close by waiting for
    378         // thread to terminate because few if any JVM's will actually
    379         // interrupt a system read() from the interrupt() method.
    380         super.close();
    381
    382         synchronized (__queue)
    383         {
    384             __hasReachedEOF = true;
    385             __isClosed = true;
    386
    387             if (__thread.isAlive())
    388             {
    389                 __thread.interrupt();
    390             }
    391
    392             __queue.notifyAll();
    393         }
    394     }
    395
    396     public void run()
    397     {
    398         int ch;
    399
    400         try
    401         {
    402 _outerLoop:
    403             while (!__isClosed)
    404             {
    405                 try
    406                 {
    407                     if ((ch = __read()) < 0)
    408                         break;
    409                 }
    410                 catch (InterruptedIOException e)
    411                 {
    412                     synchronized (__queue)
    413                     {
    414                         __ioException = e;
    415                         __queue.notifyAll();
    416                         try
    417                         {
    418                             __queue.wait(100);
    419                         }
    420                         catch (InterruptedException interrupted)
    421                         {
    422                             if (__isClosed)
    423                                 break _outerLoop;
    424                         }
    425                         continue;
    426                     }
    427                 }
    428
    429
    430                 // Critical section because we're altering __bytesAvailable,
    431                 // __queueTail, and the contents of _queue.
    432                 synchronized (__queue)
    433                 {
    434                     while (__bytesAvailable >= __queue.length - 1)
    435                     {
    436                         __queue.notify();
    437                         try
    438                         {
    439                             __queue.wait();
    440                         }
    441                         catch (InterruptedException e)
    442                         {
    443                             if (__isClosed)
    444                                 break _outerLoop;
    445                         }
    446                     }
    447
    448                     // Need to do this in case we're not full, but block on a read
    449                     if (__readIsWaiting)
    450                     {
    451                         __queue.notify();
    452                     }
    453
    454                     __queue[__queueTail] = ch;
    455                     ++__bytesAvailable;
    456
    457                     if (++__queueTail >= __queue.length)
    458                         __queueTail = 0;
    459                 }
    460             }
    461         }
    462         catch (IOException e)
    463         {
    464             synchronized (__queue)
    465             {
    466                 __ioException = e;
    467             }
    468         }
    469
    470         synchronized (__queue)
    471         {
    472             __isClosed = true; // Possibly redundant
    473             __hasReachedEOF = true;
    474             __queue.notify();
    475         }
    476     }
    477
    478
    479 }

This page was automatically generated by Maven