Home | History | Annotate | Download | only in stack
      1 /*
      2  * Conditions Of Use
      3  *
      4  * This software was developed by employees of the National Institute of
      5  * Standards and Technology (NIST), an agency of the Federal Government.
      6  * Pursuant to title 15 Untied States Code Section 105, works of NIST
      7  * employees are not subject to copyright protection in the United States
      8  * and are considered to be in the public domain.  As a result, a formal
      9  * license is not needed to use the software.
     10  *
     11  * This software is provided by NIST as a service and is expressly
     12  * provided "AS IS."  NIST MAKES NO WARRANTY OF ANY KIND, EXPRESS, IMPLIED
     13  * OR STATUTORY, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTY OF
     14  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT
     15  * AND DATA ACCURACY.  NIST does not warrant or make any representations
     16  * regarding the use of the software or the results thereof, including but
     17  * not limited to the correctness, accuracy, reliability or usefulness of
     18  * the software.
     19  *
     20  * Permission to use this software is contingent upon your acceptance
     21  * of the terms of this agreement
     22  *
     23  * .
     24  *
     25  */
     26 /******************************************************************************
     27  * Product of NIST/ITL Advanced Networking Technologies Division (ANTD).      *
     28  ******************************************************************************/
     29 package gov.nist.javax.sip.stack;
     30 
     31 import java.net.Socket;
     32 import java.net.ServerSocket;
     33 import java.io.IOException;
     34 import java.net.SocketException;
     35 import gov.nist.core.*;
     36 import java.net.*;
     37 import java.util.*;
     38 
     39 /*
     40  * Acknowledgement: Jeff Keyser suggested that a Stop mechanism be added to this. Niklas Uhrberg
     41  * suggested that a means to limit the number of simultaneous active connections should be added.
     42  * Mike Andrews suggested that the thread be accessible so as to implement clean stop using
     43  * Thread.join(). Roger M. Persson contributed a bug fix for cleanup on stop().
     44  *
     45  */
     46 
     47 /**
     48  * Sit in a loop waiting for incoming tcp connections and start a new thread to handle each new
     49  * connection. This is the active object that creates new TCP MessageChannels (one for each new
     50  * accept socket).
     51  *
     52  * @version 1.2 $Revision: 1.31 $ $Date: 2009/08/31 16:18:00 $
     53  *
     54  * @author M. Ranganathan <br/>
     55  *
     56  *
     57  */
     58 public class TCPMessageProcessor extends MessageProcessor {
     59 
     60     protected int nConnections;
     61 
     62     private boolean isRunning;
     63 
     64     private Hashtable tcpMessageChannels;
     65 
     66     private ArrayList<TCPMessageChannel> incomingTcpMessageChannels;
     67 
     68     private ServerSocket sock;
     69 
     70     protected int useCount;
     71 
     72     /**
     73      * Constructor.
     74      *
     75      * @param sipStack SIPStack structure.
     76      * @param port port where this message processor listens.
     77      */
     78     protected TCPMessageProcessor(InetAddress ipAddress, SIPTransactionStack sipStack, int port) {
     79         super(ipAddress, port, "tcp",sipStack);
     80 
     81         this.sipStack = sipStack;
     82 
     83         this.tcpMessageChannels = new Hashtable();
     84         this.incomingTcpMessageChannels = new ArrayList<TCPMessageChannel>();
     85     }
     86 
     87     /**
     88      * Start the processor.
     89      */
     90     public void start() throws IOException {
     91         Thread thread = new Thread(this);
     92         thread.setName("TCPMessageProcessorThread");
     93         thread.setPriority(Thread.MAX_PRIORITY);
     94         thread.setDaemon(true);
     95         this.sock = sipStack.getNetworkLayer().createServerSocket(getPort(), 0, getIpAddress());
     96         if (getIpAddress().getHostAddress().equals(IN_ADDR_ANY)
     97                 || getIpAddress().getHostAddress().equals(IN6_ADDR_ANY)) {
     98             // Store the address to which we are actually bound
     99             super.setIpAddress(sock.getInetAddress());
    100 
    101         }
    102         this.isRunning = true;
    103         thread.start();
    104 
    105     }
    106 
    107     /**
    108      * Run method for the thread that gets created for each accept socket.
    109      */
    110     public void run() {
    111         // Accept new connectins on our socket.
    112         while (this.isRunning) {
    113             try {
    114                 synchronized (this) {
    115                     // sipStack.maxConnections == -1 means we are
    116                     // willing to handle an "infinite" number of
    117                     // simultaneous connections (no resource limitation).
    118                     // This is the default behavior.
    119                     while (sipStack.maxConnections != -1
    120                             && this.nConnections >= sipStack.maxConnections) {
    121                         try {
    122                             this.wait();
    123 
    124                             if (!this.isRunning)
    125                                 return;
    126                         } catch (InterruptedException ex) {
    127                             break;
    128                         }
    129                     }
    130                     this.nConnections++;
    131                 }
    132 
    133                 Socket newsock = sock.accept();
    134                 if (sipStack.isLoggingEnabled()) {
    135                     getSIPStack().getStackLogger().logDebug("Accepting new connection!");
    136                 }
    137                 // Note that for an incoming message channel, the
    138                 // thread is already running
    139 
    140                 incomingTcpMessageChannels.add(new TCPMessageChannel(newsock, sipStack, this));
    141             } catch (SocketException ex) {
    142                 this.isRunning = false;
    143             } catch (IOException ex) {
    144                 // Problem accepting connection.
    145                 if (sipStack.isLoggingEnabled())
    146                     getSIPStack().getStackLogger().logException(ex);
    147                 continue;
    148             } catch (Exception ex) {
    149                 InternalErrorHandler.handleException(ex);
    150             }
    151         }
    152     }
    153 
    154     /**
    155      * Return the transport string.
    156      *
    157      * @return the transport string
    158      */
    159     public String getTransport() {
    160         return "tcp";
    161     }
    162 
    163     /**
    164      * Returns the stack.
    165      *
    166      * @return my sip stack.
    167      */
    168     public SIPTransactionStack getSIPStack() {
    169         return sipStack;
    170     }
    171 
    172     /**
    173      * Stop the message processor. Feature suggested by Jeff Keyser.
    174      */
    175     public synchronized void stop() {
    176         isRunning = false;
    177         // this.listeningPoint = null;
    178         try {
    179             sock.close();
    180         } catch (IOException e) {
    181             e.printStackTrace();
    182         }
    183 
    184         Collection en = tcpMessageChannels.values();
    185         for (Iterator it = en.iterator(); it.hasNext();) {
    186             TCPMessageChannel next = (TCPMessageChannel) it.next();
    187             next.close();
    188         }
    189         // RRPN: fix
    190         for (Iterator incomingMCIterator = incomingTcpMessageChannels.iterator(); incomingMCIterator
    191                 .hasNext();) {
    192             TCPMessageChannel next = (TCPMessageChannel) incomingMCIterator.next();
    193             next.close();
    194         }
    195 
    196         this.notify();
    197     }
    198 
    199     protected synchronized void remove(TCPMessageChannel tcpMessageChannel) {
    200 
    201         String key = tcpMessageChannel.getKey();
    202         if (sipStack.isLoggingEnabled()) {
    203             sipStack.getStackLogger().logDebug(Thread.currentThread() + " removing " + key);
    204         }
    205 
    206         /** May have been removed already */
    207         if (tcpMessageChannels.get(key) == tcpMessageChannel) {
    208             this.tcpMessageChannels.remove(key);
    209         }
    210 
    211         incomingTcpMessageChannels.remove(tcpMessageChannel);
    212     }
    213 
    214     public synchronized MessageChannel createMessageChannel(HostPort targetHostPort)
    215             throws IOException {
    216         String key = MessageChannel.getKey(targetHostPort, "TCP");
    217         if (tcpMessageChannels.get(key) != null) {
    218             return (TCPMessageChannel) this.tcpMessageChannels.get(key);
    219         } else {
    220             TCPMessageChannel retval = new TCPMessageChannel(targetHostPort.getInetAddress(),
    221                     targetHostPort.getPort(), sipStack, this);
    222             this.tcpMessageChannels.put(key, retval);
    223             retval.isCached = true;
    224             if (sipStack.isLoggingEnabled()) {
    225                 sipStack.getStackLogger().logDebug("key " + key);
    226                 sipStack.getStackLogger().logDebug("Creating " + retval);
    227             }
    228             return retval;
    229         }
    230     }
    231 
    232     protected synchronized void cacheMessageChannel(TCPMessageChannel messageChannel) {
    233         String key = messageChannel.getKey();
    234         TCPMessageChannel currentChannel = (TCPMessageChannel) tcpMessageChannels.get(key);
    235         if (currentChannel != null) {
    236             if (sipStack.isLoggingEnabled())
    237                 sipStack.getStackLogger().logDebug("Closing " + key);
    238             currentChannel.close();
    239         }
    240         if (sipStack.isLoggingEnabled())
    241             sipStack.getStackLogger().logDebug("Caching " + key);
    242         this.tcpMessageChannels.put(key, messageChannel);
    243 
    244     }
    245 
    246     public synchronized MessageChannel createMessageChannel(InetAddress host, int port)
    247             throws IOException {
    248         try {
    249             String key = MessageChannel.getKey(host, port, "TCP");
    250             if (tcpMessageChannels.get(key) != null) {
    251                 return (TCPMessageChannel) this.tcpMessageChannels.get(key);
    252             } else {
    253                 TCPMessageChannel retval = new TCPMessageChannel(host, port, sipStack, this);
    254                 this.tcpMessageChannels.put(key, retval);
    255                 retval.isCached = true;
    256                 if (sipStack.isLoggingEnabled()) {
    257                     sipStack.getStackLogger().logDebug("key " + key);
    258                     sipStack.getStackLogger().logDebug("Creating " + retval);
    259                 }
    260                 return retval;
    261             }
    262         } catch (UnknownHostException ex) {
    263             throw new IOException(ex.getMessage());
    264         }
    265     }
    266 
    267     /**
    268      * TCP can handle an unlimited number of bytes.
    269      */
    270     public int getMaximumMessageSize() {
    271         return Integer.MAX_VALUE;
    272     }
    273 
    274     public boolean inUse() {
    275         return this.useCount != 0;
    276     }
    277 
    278     /**
    279      * Default target port for TCP
    280      */
    281     public int getDefaultTargetPort() {
    282         return 5060;
    283     }
    284 
    285     /**
    286      * TCP is not a secure protocol.
    287      */
    288     public boolean isSecure() {
    289         return false;
    290     }
    291 }
    292