Home | History | Annotate | Download | only in sip
      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 package gov.nist.javax.sip;
     27 
     28 import java.util.*;
     29 import gov.nist.javax.sip.stack.*;
     30 import gov.nist.javax.sip.message.*;
     31 import javax.sip.message.*;
     32 import javax.sip.*;
     33 import gov.nist.core.ThreadAuditor;
     34 
     35 /* bug fixes SIPQuest communications and Shu-Lin Chen. */
     36 
     37 /**
     38  * Event Scanner to deliver events to the Listener.
     39  *
     40  * @version 1.2 $Revision: 1.41 $ $Date: 2009/11/18 02:35:17 $
     41  *
     42  * @author M. Ranganathan <br/>
     43  *
     44  *
     45  */
     46 class EventScanner implements Runnable {
     47 
     48     private boolean isStopped;
     49 
     50     private int refCount;
     51 
     52     // SIPquest: Fix for deadlocks
     53     private LinkedList pendingEvents = new LinkedList();
     54 
     55     private int[] eventMutex = { 0 };
     56 
     57     private SipStackImpl sipStack;
     58 
     59     public void incrementRefcount() {
     60         synchronized (eventMutex) {
     61             this.refCount++;
     62         }
     63     }
     64 
     65     public EventScanner(SipStackImpl sipStackImpl) {
     66         this.pendingEvents = new LinkedList();
     67         Thread myThread = new Thread(this);
     68         // This needs to be set to false else the
     69         // main thread mysteriously exits.
     70         myThread.setDaemon(false);
     71 
     72         this.sipStack = sipStackImpl;
     73 
     74         myThread.setName("EventScannerThread");
     75 
     76         myThread.start();
     77 
     78     }
     79 
     80     public void addEvent(EventWrapper eventWrapper) {
     81     	if (sipStack.isLoggingEnabled())
     82     		sipStack.getStackLogger().logDebug("addEvent " + eventWrapper);
     83         synchronized (this.eventMutex) {
     84 
     85             pendingEvents.add(eventWrapper);
     86 
     87             // Add the event into the pending events list
     88 
     89             eventMutex.notify();
     90         }
     91 
     92     }
     93 
     94     /**
     95      * Stop the event scanner. Decrement the reference count and exit the
     96      * scanner thread if the ref count goes to 0.
     97      */
     98 
     99     public void stop() {
    100         synchronized (eventMutex) {
    101 
    102             if (this.refCount > 0)
    103                 this.refCount--;
    104 
    105             if (this.refCount == 0) {
    106                 isStopped = true;
    107                 eventMutex.notify();
    108 
    109             }
    110         }
    111     }
    112 
    113     /**
    114      * Brutally stop the event scanner. This does not wait for the refcount to
    115      * go to 0.
    116      *
    117      */
    118     public void forceStop() {
    119         synchronized (this.eventMutex) {
    120             this.isStopped = true;
    121             this.refCount = 0;
    122             this.eventMutex.notify();
    123         }
    124 
    125     }
    126 
    127     public void deliverEvent(EventWrapper eventWrapper) {
    128         EventObject sipEvent = eventWrapper.sipEvent;
    129         if (sipStack.isLoggingEnabled())
    130             sipStack.getStackLogger().logDebug(
    131                     "sipEvent = " + sipEvent + "source = "
    132                             + sipEvent.getSource());
    133         SipListener sipListener = null;
    134 
    135         if (!(sipEvent instanceof IOExceptionEvent)) {
    136             sipListener = ((SipProviderImpl) sipEvent.getSource()).getSipListener();
    137         } else {
    138             sipListener = sipStack.getSipListener();
    139         }
    140 
    141         if (sipEvent instanceof RequestEvent) {
    142             try {
    143                 // Check if this request has already created a
    144                 // transaction
    145                 SIPRequest sipRequest = (SIPRequest) ((RequestEvent) sipEvent)
    146                         .getRequest();
    147 
    148                 if (sipStack.isLoggingEnabled()) {
    149                     sipStack.getStackLogger().logDebug(
    150                             "deliverEvent : "
    151                                     + sipRequest.getFirstLine()
    152                                     + " transaction "
    153                                     + eventWrapper.transaction
    154                                     + " sipEvent.serverTx = "
    155                                     + ((RequestEvent) sipEvent)
    156                                             .getServerTransaction());
    157                 }
    158 
    159                 // Discard the duplicate request if a
    160                 // transaction already exists. If the listener chose
    161                 // to handle the request statelessly, then the listener
    162                 // will see the retransmission.
    163                 // Note that in both of these two cases, JAIN SIP will allow
    164                 // you to handle the request statefully or statelessly.
    165                 // An example of the latter case is REGISTER and an example
    166                 // of the former case is INVITE.
    167 
    168                 SIPServerTransaction tx = (SIPServerTransaction) sipStack
    169                         .findTransaction(sipRequest, true);
    170 
    171                 if (tx != null && !tx.passToListener()) {
    172 
    173                     // JvB: make an exception for a very rare case: some
    174                     // (broken) UACs use
    175                     // the same branch parameter for an ACK. Such an ACK should
    176                     // be passed
    177                     // to the listener (tx == INVITE ST, terminated upon sending
    178                     // 2xx but
    179                     // lingering to catch retransmitted INVITEs)
    180                     if (sipRequest.getMethod().equals(Request.ACK)
    181                             && tx.isInviteTransaction() &&
    182                             ( tx.getLastResponse().getStatusCode()/100 == 2 ||
    183                                 sipStack.isNon2XXAckPassedToListener())) {
    184 
    185                         if (sipStack.isLoggingEnabled())
    186                             sipStack
    187                                     .getStackLogger()
    188                                     .logDebug(
    189                                             "Detected broken client sending ACK with same branch! Passing...");
    190                     } else {
    191                         if (sipStack.isLoggingEnabled())
    192                             sipStack.getStackLogger().logDebug(
    193                                     "transaction already exists! " + tx);
    194                         return;
    195                     }
    196                 } else if (sipStack.findPendingTransaction(sipRequest) != null) {
    197                     if (sipStack.isLoggingEnabled())
    198                         sipStack.getStackLogger().logDebug(
    199                                 "transaction already exists!!");
    200 
    201                     return;
    202                 } else {
    203                     // Put it in the pending list so that if a repeat
    204                     // request comes along it will not get assigned a
    205                     // new transaction
    206                     SIPServerTransaction st = (SIPServerTransaction) eventWrapper.transaction;
    207                     sipStack.putPendingTransaction(st);
    208                 }
    209 
    210                 // Set up a pointer to the transaction.
    211                 sipRequest.setTransaction(eventWrapper.transaction);
    212                 // Change made by SIPquest
    213                 try {
    214 
    215                     if (sipStack.isLoggingEnabled()) {
    216                         sipStack.getStackLogger()
    217                                 .logDebug(
    218                                         "Calling listener "
    219                                                 + sipRequest.getFirstLine());
    220                         sipStack.getStackLogger().logDebug(
    221                                 "Calling listener " + eventWrapper.transaction);
    222                     }
    223                     if (sipListener != null)
    224                         sipListener.processRequest((RequestEvent) sipEvent);
    225 
    226                     if (sipStack.isLoggingEnabled()) {
    227                         sipStack.getStackLogger().logDebug(
    228                                 "Done processing Message "
    229                                         + sipRequest.getFirstLine());
    230                     }
    231                     if (eventWrapper.transaction != null) {
    232 
    233                         SIPDialog dialog = (SIPDialog) eventWrapper.transaction
    234                                 .getDialog();
    235                         if (dialog != null)
    236                             dialog.requestConsumed();
    237 
    238                     }
    239                 } catch (Exception ex) {
    240                     // We cannot let this thread die under any
    241                     // circumstances. Protect ourselves by logging
    242                     // errors to the console but continue.
    243                     sipStack.getStackLogger().logException(ex);
    244                 }
    245             } finally {
    246                 if (sipStack.isLoggingEnabled()) {
    247                     sipStack.getStackLogger().logDebug(
    248                             "Done processing Message "
    249                                     + ((SIPRequest) (((RequestEvent) sipEvent)
    250                                             .getRequest())).getFirstLine());
    251                 }
    252                 if (eventWrapper.transaction != null
    253                         && ((SIPServerTransaction) eventWrapper.transaction)
    254                                 .passToListener()) {
    255                     ((SIPServerTransaction) eventWrapper.transaction)
    256                             .releaseSem();
    257                 }
    258 
    259                 if (eventWrapper.transaction != null)
    260                     sipStack
    261                             .removePendingTransaction((SIPServerTransaction) eventWrapper.transaction);
    262                 if (eventWrapper.transaction.getOriginalRequest().getMethod()
    263                         .equals(Request.ACK)) {
    264                     // Set the tx state to terminated so it is removed from the
    265                     // stack
    266                     // if the user configured to get notification on ACK
    267                     // termination
    268                     eventWrapper.transaction
    269                             .setState(TransactionState.TERMINATED);
    270                 }
    271             }
    272 
    273         } else if (sipEvent instanceof ResponseEvent) {
    274             try {
    275                 ResponseEvent responseEvent = (ResponseEvent) sipEvent;
    276                 SIPResponse sipResponse = (SIPResponse) responseEvent
    277                         .getResponse();
    278                 SIPDialog sipDialog = ((SIPDialog) responseEvent.getDialog());
    279                 try {
    280                     if (sipStack.isLoggingEnabled()) {
    281 
    282                         sipStack.getStackLogger().logDebug(
    283                                 "Calling listener for "
    284                                         + sipResponse.getFirstLine());
    285                     }
    286                     if (sipListener != null) {
    287                         SIPTransaction tx = eventWrapper.transaction;
    288                         if (tx != null) {
    289                             tx.setPassToListener();
    290                         }
    291                         sipListener.processResponse((ResponseEvent) sipEvent);
    292                     }
    293 
    294                     /*
    295                      * If the response for a request within a dialog is a 481
    296                      * (Call/Transaction Does Not Exist) or a 408 (Request
    297                      * Timeout), the UAC SHOULD terminate the dialog.
    298                      */
    299                     if ((sipDialog != null && (sipDialog.getState() == null || !sipDialog
    300                             .getState().equals(DialogState.TERMINATED)))
    301                             && (sipResponse.getStatusCode() == Response.CALL_OR_TRANSACTION_DOES_NOT_EXIST || sipResponse
    302                                     .getStatusCode() == Response.REQUEST_TIMEOUT)) {
    303                         if (sipStack.isLoggingEnabled()) {
    304                             sipStack.getStackLogger().logDebug(
    305                                     "Removing dialog on 408 or 481 response");
    306                         }
    307                         sipDialog.doDeferredDelete();
    308                     }
    309 
    310                     /*
    311                      * The Client tx disappears after the first 2xx response
    312                      * However, additional 2xx responses may arrive later for
    313                      * example in the following scenario:
    314                      *
    315                      * Multiple 2xx responses may arrive at the UAC for a single
    316                      * INVITE request due to a forking proxy. Each response is
    317                      * distinguished by the tag parameter in the To header
    318                      * field, and each represents a distinct dialog, with a
    319                      * distinct dialog identifier.
    320                      *
    321                      * If the Listener does not ACK the 200 then we assume he
    322                      * does not care about the dialog and gc the dialog after
    323                      * some time. However, this is really an application bug.
    324                      * This garbage collects unacknowledged dialogs.
    325                      *
    326                      */
    327                     if (sipResponse.getCSeq().getMethod()
    328                             .equals(Request.INVITE)
    329                             && sipDialog != null
    330                             && sipResponse.getStatusCode() == 200) {
    331                         if (sipStack.isLoggingEnabled()) {
    332                             sipStack.getStackLogger().logDebug(
    333                                     "Warning! unacknowledged dialog. " + sipDialog.getState());
    334                         }
    335                         /*
    336                          * If we dont see an ACK in 32 seconds, we want to tear down the dialog.
    337                          */
    338                         sipDialog.doDeferredDeleteIfNoAckSent(sipResponse.getCSeq().getSeqNumber());
    339                     }
    340                 } catch (Exception ex) {
    341                     // We cannot let this thread die under any
    342                     // circumstances. Protect ourselves by logging
    343                     // errors to the console but continue.
    344                     sipStack.getStackLogger().logException(ex);
    345                 }
    346                 // The original request is not needed except for INVITE
    347                 // transactions -- null the pointers to the transactions so
    348                 // that state may be released.
    349                 SIPClientTransaction ct = (SIPClientTransaction) eventWrapper.transaction;
    350                 if (ct != null
    351                         && TransactionState.COMPLETED == ct.getState()
    352                         && ct.getOriginalRequest() != null
    353                         && !ct.getOriginalRequest().getMethod().equals(
    354                                 Request.INVITE)) {
    355                     // reduce the state to minimum
    356                     // This assumes that the application will not need
    357                     // to access the request once the transaction is
    358                     // completed.
    359                     ct.clearState();
    360                 }
    361                 // mark no longer in the event queue.
    362             } finally {
    363                 if (eventWrapper.transaction != null
    364                         && eventWrapper.transaction.passToListener()) {
    365                     eventWrapper.transaction.releaseSem();
    366                 }
    367             }
    368 
    369         } else if (sipEvent instanceof TimeoutEvent) {
    370             // Change made by SIPquest
    371             try {
    372                 // Check for null as listener could be removed.
    373                 if (sipListener != null)
    374                     sipListener.processTimeout((TimeoutEvent) sipEvent);
    375             } catch (Exception ex) {
    376                 // We cannot let this thread die under any
    377                 // circumstances. Protect ourselves by logging
    378                 // errors to the console but continue.
    379                 sipStack.getStackLogger().logException(ex);
    380             }
    381 
    382         } else if (sipEvent instanceof DialogTimeoutEvent) {
    383             try {
    384                 // Check for null as listener could be removed.
    385                 if (sipListener != null && sipListener instanceof SipListenerExt) {
    386                     ((SipListenerExt)sipListener).processDialogTimeout((DialogTimeoutEvent) sipEvent);
    387                 }
    388             } catch (Exception ex) {
    389                 // We cannot let this thread die under any
    390                 // circumstances. Protect ourselves by logging
    391                 // errors to the console but continue.
    392                 sipStack.getStackLogger().logException(ex);
    393             }
    394 
    395         } else if (sipEvent instanceof IOExceptionEvent) {
    396             try {
    397                 if (sipListener != null)
    398                     sipListener.processIOException((IOExceptionEvent) sipEvent);
    399             } catch (Exception ex) {
    400                 sipStack.getStackLogger().logException(ex);
    401             }
    402         } else if (sipEvent instanceof TransactionTerminatedEvent) {
    403             try {
    404                 if (sipStack.isLoggingEnabled()) {
    405                     sipStack.getStackLogger().logDebug(
    406                             "About to deliver transactionTerminatedEvent");
    407                     sipStack.getStackLogger().logDebug(
    408                             "tx = "
    409                                     + ((TransactionTerminatedEvent) sipEvent)
    410                                             .getClientTransaction());
    411                     sipStack.getStackLogger().logDebug(
    412                             "tx = "
    413                                     + ((TransactionTerminatedEvent) sipEvent)
    414                                             .getServerTransaction());
    415 
    416                 }
    417                 if (sipListener != null)
    418                     sipListener
    419                             .processTransactionTerminated((TransactionTerminatedEvent) sipEvent);
    420             } catch (AbstractMethodError ame) {
    421                 // JvB: for backwards compatibility, accept this
    422             	if (sipStack.isLoggingEnabled())
    423             		sipStack
    424                         .getStackLogger()
    425                         .logWarning(
    426                                 "Unable to call sipListener.processTransactionTerminated");
    427             } catch (Exception ex) {
    428                 sipStack.getStackLogger().logException(ex);
    429             }
    430         } else if (sipEvent instanceof DialogTerminatedEvent) {
    431             try {
    432                 if (sipListener != null)
    433                     sipListener
    434                             .processDialogTerminated((DialogTerminatedEvent) sipEvent);
    435             } catch (AbstractMethodError ame) {
    436                 // JvB: for backwards compatibility, accept this
    437             	if (sipStack.isLoggingEnabled())
    438             		sipStack.getStackLogger().logWarning(
    439                         "Unable to call sipListener.processDialogTerminated");
    440             } catch (Exception ex) {
    441                 sipStack.getStackLogger().logException(ex);
    442             }
    443         } else {
    444 
    445             sipStack.getStackLogger().logFatalError("bad event" + sipEvent);
    446         }
    447 
    448     }
    449 
    450     /**
    451      * For the non-re-entrant listener this delivers the events to the listener
    452      * from a single queue. If the listener is re-entrant, then the stack just
    453      * calls the deliverEvent method above.
    454      */
    455 
    456     public void run() {
    457         try {
    458             // Ask the auditor to monitor this thread
    459             ThreadAuditor.ThreadHandle threadHandle = sipStack.getThreadAuditor().addCurrentThread();
    460 
    461             while (true) {
    462                 EventWrapper eventWrapper = null;
    463 
    464                 LinkedList eventsToDeliver;
    465                 synchronized (this.eventMutex) {
    466                     // First, wait for some events to become available.
    467                     while (pendingEvents.isEmpty()) {
    468                         // There's nothing in the list, check to make sure we
    469                         // haven't
    470                         // been stopped. If we have, then let the thread die.
    471                         if (this.isStopped) {
    472                             if (sipStack.isLoggingEnabled())
    473                                 sipStack.getStackLogger().logDebug(
    474                                         "Stopped event scanner!!");
    475                             return;
    476                         }
    477 
    478                         // We haven't been stopped, and the event list is indeed
    479                         // rather empty. Wait for some events to come along.
    480                         try {
    481                             // Send a heartbeat to the thread auditor
    482                             threadHandle.ping();
    483 
    484                             // Wait for events (with a timeout)
    485                             eventMutex.wait(threadHandle.getPingIntervalInMillisecs());
    486                         } catch (InterruptedException ex) {
    487                             // Let the thread die a normal death
    488                         	if (sipStack.isLoggingEnabled())
    489                         		sipStack.getStackLogger().logDebug("Interrupted!");
    490                             return;
    491                         }
    492                     }
    493 
    494                     // There are events in the 'pending events list' that need
    495                     // processing. Hold onto the old 'pending Events' list, but
    496                     // make a new one for the other methods to operate on. This
    497                     // tap-dancing is to avoid deadlocks and also to ensure that
    498                     // the list is not modified while we are iterating over it.
    499                     eventsToDeliver = pendingEvents;
    500                     pendingEvents = new LinkedList();
    501                 }
    502                 ListIterator iterator = eventsToDeliver.listIterator();
    503                 while (iterator.hasNext()) {
    504                     eventWrapper = (EventWrapper) iterator.next();
    505                     if (sipStack.isLoggingEnabled()) {
    506                         sipStack.getStackLogger().logDebug(
    507                                 "Processing " + eventWrapper + "nevents "
    508                                         + eventsToDeliver.size());
    509                     }
    510                     try {
    511                         deliverEvent(eventWrapper);
    512                     } catch (Exception e) {
    513                         if (sipStack.isLoggingEnabled()) {
    514                             sipStack.getStackLogger().logError(
    515                                     "Unexpected exception caught while delivering event -- carrying on bravely", e);
    516                         }
    517                     }
    518                 }
    519             } // end While
    520         } finally {
    521             if (sipStack.isLoggingEnabled()) {
    522                 if (!this.isStopped) {
    523                     sipStack.getStackLogger().logFatalError("Event scanner exited abnormally");
    524                 }
    525             }
    526         }
    527     }
    528 
    529 }
    530