• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
incrementRefcount()59     public void incrementRefcount() {
60         synchronized (eventMutex) {
61             this.refCount++;
62         }
63     }
64 
EventScanner(SipStackImpl sipStackImpl)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 
addEvent(EventWrapper eventWrapper)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 
stop()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      */
forceStop()118     public void forceStop() {
119         synchronized (this.eventMutex) {
120             this.isStopped = true;
121             this.refCount = 0;
122             this.eventMutex.notify();
123         }
124 
125     }
126 
deliverEvent(EventWrapper eventWrapper)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 
run()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