/** * Place to put some hacks if needed on incoming requests. * * @param event the incoming request event. * @return status <code>true</code> if we don't need to process this message, just discard it and * <code>false</code> otherwise. */ private boolean applyNonConformanceHacks(RequestEvent event) { Request request = event.getRequest(); try { /* * Max-Forwards is required, yet there are UAs which do not * place it. SipProvider#getNewServerTransaction(Request) * will throw an exception in the case of a missing * Max-Forwards header and this method will eventually just * log it thus ignoring the whole event. */ if (request.getHeader(MaxForwardsHeader.NAME) == null) { // it appears that some buggy providers do send requests // with no Max-Forwards headers, as we are at application level // and we know there will be no endless loops // there is no problem of adding headers and process normally // this messages MaxForwardsHeader maxForwards = SipFactory.getInstance().createHeaderFactory().createMaxForwardsHeader(70); request.setHeader(maxForwards); } } catch (Throwable ex) { logger.warn("Cannot apply incoming request modification!", ex); } try { // using asterisk voice mail initial notify for messages // is ok, but on the fly received messages their notify comes // without subscription-state, so we add it in order to be able to // process message. if (request.getMethod().equals(Request.NOTIFY) && request.getHeader(EventHeader.NAME) != null && ((EventHeader) request.getHeader(EventHeader.NAME)) .getEventType() .equals(OperationSetMessageWaitingSipImpl.EVENT_PACKAGE) && request.getHeader(SubscriptionStateHeader.NAME) == null) { request.addHeader( new HeaderFactoryImpl().createSubscriptionStateHeader(SubscriptionStateHeader.ACTIVE)); } } catch (Throwable ex) { logger.warn("Cannot apply incoming request modification!", ex); } try { // receiving notify message without subscription state // used for keep-alive pings, they have done their job // and are no more need. Skip processing them to avoid // filling logs with unneeded exceptions. if (request.getMethod().equals(Request.NOTIFY) && request.getHeader(SubscriptionStateHeader.NAME) == null) { return true; } } catch (Throwable ex) { logger.warn("Cannot apply incoming request modification!", ex); } return false; }
/** * Try to find a charset in a MESSAGE request for the text content. If no charset is defined, * the default charset for text messages is returned. * * @param req the MESSAGE request in which to look for a charset * @return defined charset in the request or DEFAULT_MIME_ENCODING if no charset is specified */ private String getCharset(Request req) { String charset = null; Header contentTypeHeader = req.getHeader(ContentTypeHeader.NAME); if (contentTypeHeader instanceof ContentTypeHeader) charset = ((ContentTypeHeader) contentTypeHeader).getParameter("charset"); if (charset == null) charset = DEFAULT_MIME_ENCODING; return charset; }
/** * Sends <tt>messageRequest</tt> to the specified destination and logs <tt>messageContent</tt> for * later use. * * @param messageRequest the <tt>SipRequest</tt> that we are about to send. * @param to the Contact that we are sending <tt>messageRequest</tt> to. * @param messageContent the SC <tt>Message</tt> that was used to create the <tt>Request</tt> . * @throws TransactionUnavailableException if we fail creating the transaction required to send * <tt>messageRequest</tt>. * @throws SipException if we fail sending <tt>messageRequest</tt>. */ void sendMessageRequest(Request messageRequest, Contact to, Message messageContent) throws TransactionUnavailableException, SipException { // Transaction ClientTransaction messageTransaction; SipProvider jainSipProvider = this.sipProvider.getDefaultJainSipProvider(); messageTransaction = jainSipProvider.getNewClientTransaction(messageRequest); // send the message messageTransaction.sendRequest(); // we register the reference to this message to retrieve it when // we'll receive the response message String key = ((CallIdHeader) messageRequest.getHeader(CallIdHeader.NAME)).getCallId(); this.sentMsg.put(key, messageContent); }
@Override public void run() { try { logger.logEntry(); // From FromHeader fromHeader = null; try { // this keep alive task only makes sense in case we have // a registrar so we deliberately use our AOR and do not // use the getOurSipAddress() method. fromHeader = provider .getHeaderFactory() .createFromHeader( provider.getRegistrarConnection().getAddressOfRecord(), SipMessageFactory.generateLocalTag()); } catch (ParseException ex) { // this should never happen so let's just log and bail. logger.error("Failed to generate a from header for " + "our register request.", ex); return; } // Call ID Header CallIdHeader callIdHeader = provider.getDefaultJainSipProvider().getNewCallId(); // CSeq Header CSeqHeader cSeqHeader = null; try { cSeqHeader = provider.getHeaderFactory().createCSeqHeader(getNextCSeqValue(), Request.OPTIONS); } catch (ParseException ex) { // Should never happen logger.error("Corrupt Sip Stack", ex); return; } catch (InvalidArgumentException ex) { // Should never happen logger.error("The application is corrupt", ex); return; } // To Header ToHeader toHeader = null; try { // this request isn't really going anywhere so we put our // own address in the To Header. toHeader = provider.getHeaderFactory().createToHeader(fromHeader.getAddress(), null); } catch (ParseException ex) { logger.error("Could not create a To header for address:" + fromHeader.getAddress(), ex); return; } // MaxForwardsHeader MaxForwardsHeader maxForwardsHeader = provider.getMaxForwardsHeader(); // Request Request request = null; try { // create a host-only uri for the request uri header. String domain = ((SipURI) toHeader.getAddress().getURI()).getHost(); // request URI SipURI requestURI = provider.getAddressFactory().createSipURI(null, domain); // Via Headers ArrayList<ViaHeader> viaHeaders = provider.getLocalViaHeaders(requestURI); request = provider .getMessageFactory() .createRequest( requestURI, Request.OPTIONS, callIdHeader, cSeqHeader, fromHeader, toHeader, viaHeaders, maxForwardsHeader); if (logger.isDebugEnabled()) logger.debug("Created OPTIONS request " + request); } catch (ParseException ex) { logger.error("Could not create an OPTIONS request!", ex); return; } Iterator<String> supportedMethods = provider.getSupportedMethods().iterator(); // add to the allows header all methods that we support while (supportedMethods.hasNext()) { String method = supportedMethods.next(); // don't support REGISTERs if (method.equals(Request.REGISTER)) continue; request.addHeader(provider.getHeaderFactory().createAllowHeader(method)); } Iterator<String> events = provider.getKnownEventsList().iterator(); synchronized (provider.getKnownEventsList()) { while (events.hasNext()) { String event = events.next(); request.addHeader(provider.getHeaderFactory().createAllowEventsHeader(event)); } } // Transaction ClientTransaction optionsTrans = null; try { optionsTrans = provider.getDefaultJainSipProvider().getNewClientTransaction(request); } catch (TransactionUnavailableException ex) { logger.error("Could not create options transaction!\n", ex); return; } try { optionsTrans.sendRequest(); if (logger.isDebugEnabled()) logger.debug("sent request= " + request); } catch (SipException ex) { logger.error("Could not send out the options request!", ex); if (ex.getCause() instanceof IOException) { // IOException problem with network disconnect(); } return; } } catch (Exception ex) { logger.error("Cannot send OPTIONS keep alive", ex); } }
/** * Process a response from a distant contact. * * @param responseEvent the <tt>ResponseEvent</tt> containing the newly received SIP response. * @return <tt>true</tt> if the specified event has been handled by this processor and shouldn't * be offered to other processors registered for the same method; <tt>false</tt>, otherwise */ @Override public boolean processResponse(ResponseEvent responseEvent) { synchronized (messageProcessors) { for (SipMessageProcessor listener : messageProcessors) if (!listener.processResponse(responseEvent, sentMsg)) return true; } Request req = responseEvent.getClientTransaction().getRequest(); int status = responseEvent.getResponse().getStatusCode(); // content of the response String content = null; try { content = new String(req.getRawContent(), getCharset(req)); } catch (UnsupportedEncodingException exc) { if (logger.isDebugEnabled()) logger.debug("failed to convert the message charset", exc); content = new String(req.getRawContent()); } // to who did we send the original message ? ToHeader toHeader = (ToHeader) req.getHeader(ToHeader.NAME); if (toHeader == null) { // should never happen logger.error("send a request without a to header"); return false; } Contact to = opSetPersPresence.resolveContactID(toHeader.getAddress().getURI().toString()); if (to == null) { logger.error( "Error received a response from an unknown contact : " + toHeader.getAddress().getURI().toString() + " : " + responseEvent.getResponse().getStatusCode() + " " + responseEvent.getResponse().getReasonPhrase()); // error for delivering the message fireMessageDeliveryFailed( // we don't know what message it concerns createMessage(content), to, MessageDeliveryFailedEvent.INTERNAL_ERROR); return false; } // we retrieve the original message String key = ((CallIdHeader) req.getHeader(CallIdHeader.NAME)).getCallId(); Message newMessage = sentMsg.get(key); if (newMessage == null) { // should never happen logger.error("Couldn't find the message sent"); // error for delivering the message fireMessageDeliveryFailed( // we don't know what message it is createMessage(content), to, MessageDeliveryFailedEvent.INTERNAL_ERROR); return true; } // status 401/407 = proxy authentification if (status >= 400 && status != 401 && status != 407) { if (logger.isInfoEnabled()) logger.info( responseEvent.getResponse().getStatusCode() + " " + responseEvent.getResponse().getReasonPhrase()); // error for delivering the message MessageDeliveryFailedEvent evt = new MessageDeliveryFailedEvent( newMessage, to, MessageDeliveryFailedEvent.NETWORK_FAILURE, System.currentTimeMillis(), responseEvent.getResponse().getStatusCode() + " " + responseEvent.getResponse().getReasonPhrase()); fireMessageEvent(evt); sentMsg.remove(key); } else if (status == 401 || status == 407) { // proxy ask for authentification if (logger.isDebugEnabled()) logger.debug( "proxy asks authentication : " + responseEvent.getResponse().getStatusCode() + " " + responseEvent.getResponse().getReasonPhrase()); ClientTransaction clientTransaction = responseEvent.getClientTransaction(); SipProvider sourceProvider = (SipProvider) responseEvent.getSource(); try { processAuthenticationChallenge( clientTransaction, responseEvent.getResponse(), sourceProvider); } catch (OperationFailedException ex) { logger.error("can't solve the challenge", ex); // error for delivering the message MessageDeliveryFailedEvent evt = new MessageDeliveryFailedEvent( newMessage, to, MessageDeliveryFailedEvent.NETWORK_FAILURE, System.currentTimeMillis(), ex.getMessage()); fireMessageEvent(evt); sentMsg.remove(key); } } else if (status >= 200) { if (logger.isDebugEnabled()) logger.debug( "Ack received from the network : " + responseEvent.getResponse().getStatusCode() + " " + responseEvent.getResponse().getReasonPhrase()); // we delivered the message MessageDeliveredEvent msgDeliveredEvt = new MessageDeliveredEvent(newMessage, to, System.currentTimeMillis()); fireMessageEvent(msgDeliveredEvt); // we don't need this message anymore sentMsg.remove(key); } return true; }
/** * Process a request from a distant contact * * @param requestEvent the <tt>RequestEvent</tt> containing the newly received request. * @return <tt>true</tt> if the specified event has been handled by this processor and shouldn't * be offered to other processors registered for the same method; <tt>false</tt>, otherwise */ @Override public boolean processRequest(RequestEvent requestEvent) { synchronized (messageProcessors) { for (SipMessageProcessor listener : messageProcessors) if (!listener.processMessage(requestEvent)) return true; } // get the content String content = null; Request req = requestEvent.getRequest(); try { content = new String(req.getRawContent(), getCharset(req)); } catch (UnsupportedEncodingException ex) { if (logger.isDebugEnabled()) logger.debug("failed to convert the message charset"); content = new String(requestEvent.getRequest().getRawContent()); } // who sent this request ? FromHeader fromHeader = (FromHeader) requestEvent.getRequest().getHeader(FromHeader.NAME); if (fromHeader == null) { logger.error("received a request without a from header"); return false; } Contact from = opSetPersPresence.resolveContactID(fromHeader.getAddress().getURI().toString()); ContentTypeHeader ctheader = (ContentTypeHeader) req.getHeader(ContentTypeHeader.NAME); String ctype = null; String cencoding = null; if (ctheader == null) { ctype = DEFAULT_MIME_TYPE; } else { ctype = ctheader.getContentType() + "/" + ctheader.getContentSubType(); cencoding = ctheader.getParameter("charset"); } if (cencoding == null) cencoding = DEFAULT_MIME_ENCODING; Message newMessage = createMessage(content, ctype, cencoding, null); if (from == null) { if (logger.isDebugEnabled()) logger.debug( "received a message from an unknown contact: " + fromHeader.getAddress().getURI().toString()); // create the volatile contact from = opSetPersPresence.createVolatileContact(fromHeader.getAddress().getURI().toString()); } // answer ok try { Response ok = sipProvider.getMessageFactory().createResponse(Response.OK, requestEvent.getRequest()); SipStackSharing.getOrCreateServerTransaction(requestEvent).sendResponse(ok); } catch (ParseException exc) { logger.error("failed to build the response", exc); } catch (SipException exc) { logger.error("failed to send the response : " + exc.getMessage(), exc); } catch (InvalidArgumentException exc) { if (logger.isDebugEnabled()) logger.debug("Invalid argument for createResponse : " + exc.getMessage(), exc); } // fire an event MessageReceivedEvent msgReceivedEvt = new MessageReceivedEvent(newMessage, from, System.currentTimeMillis()); fireMessageEvent(msgReceivedEvt); return true; }
@Override public boolean processTimeout(TimeoutEvent timeoutEvent) { synchronized (messageProcessors) { for (SipMessageProcessor listener : messageProcessors) if (!listener.processTimeout(timeoutEvent, sentMsg)) return true; } // this is normaly handled by the SIP stack logger.error("Timeout event thrown : " + timeoutEvent.toString()); if (timeoutEvent.isServerTransaction()) { logger.warn("The sender has probably not received our OK"); return false; } Request req = timeoutEvent.getClientTransaction().getRequest(); // get the content String content = null; try { content = new String(req.getRawContent(), getCharset(req)); } catch (UnsupportedEncodingException ex) { logger.warn("failed to convert the message charset", ex); content = new String(req.getRawContent()); } // to who this request has been sent ? ToHeader toHeader = (ToHeader) req.getHeader(ToHeader.NAME); if (toHeader == null) { logger.error("received a request without a to header"); return false; } Contact to = opSetPersPresence.resolveContactID(toHeader.getAddress().getURI().toString()); Message failedMessage = null; if (to == null) { logger.error( "timeout on a message sent to an unknown contact : " + toHeader.getAddress().getURI().toString()); // we don't know what message it concerns, so create a new // one failedMessage = createMessage(content); } else { // try to retrieve the original message String key = ((CallIdHeader) req.getHeader(CallIdHeader.NAME)).getCallId(); failedMessage = sentMsg.get(key); if (failedMessage == null) { // should never happen logger.error("Couldn't find the sent message."); // we don't know what the message is so create a new one // based on the content of the failed request. failedMessage = createMessage(content); } } // error for delivering the message fireMessageDeliveryFailed( // we don't know what message it concerns failedMessage, to, MessageDeliveryFailedEvent.INTERNAL_ERROR); return true; }
/** * Construct a <tt>Request</tt> represent a new message. * * @param to the <tt>Contact</tt> to send <tt>message</tt> to * @param message the <tt>Message</tt> to send. * @return a Message Request destined to the contact * @throws OperationFailedException if an error occurred during the creation of the request */ Request createMessageRequest(Contact to, Message message) throws OperationFailedException { Address toAddress = null; try { toAddress = sipProvider.parseAddressString(to.getAddress()); } catch (ParseException exc) { // Shouldn't happen logger.error("An unexpected error occurred while" + "constructing the address", exc); throw new OperationFailedException( "An unexpected error occurred while" + "constructing the address", OperationFailedException.INTERNAL_ERROR, exc); } // Call ID CallIdHeader callIdHeader = this.sipProvider.getDefaultJainSipProvider().getNewCallId(); // CSeq CSeqHeader cSeqHeader = null; try { // protect seqN synchronized (this) { cSeqHeader = this.sipProvider.getHeaderFactory().createCSeqHeader(seqN++, Request.MESSAGE); } } catch (InvalidArgumentException ex) { // Shouldn't happen logger.error("An unexpected error occurred while" + "constructing the CSeqHeadder", ex); throw new OperationFailedException( "An unexpected error occurred while" + "constructing the CSeqHeadder", OperationFailedException.INTERNAL_ERROR, ex); } catch (ParseException exc) { // shouldn't happen logger.error("An unexpected error occurred while" + "constructing the CSeqHeadder", exc); throw new OperationFailedException( "An unexpected error occurred while" + "constructing the CSeqHeadder", OperationFailedException.INTERNAL_ERROR, exc); } // FromHeader and ToHeader String localTag = SipMessageFactory.generateLocalTag(); FromHeader fromHeader = null; ToHeader toHeader = null; try { // FromHeader fromHeader = this.sipProvider .getHeaderFactory() .createFromHeader(sipProvider.getOurSipAddress(toAddress), localTag); // ToHeader toHeader = this.sipProvider.getHeaderFactory().createToHeader(toAddress, null); } catch (ParseException ex) { // these two should never happen. logger.error( "An unexpected error occurred while" + "constructing the FromHeader or ToHeader", ex); throw new OperationFailedException( "An unexpected error occurred while" + "constructing the FromHeader or ToHeader", OperationFailedException.INTERNAL_ERROR, ex); } // ViaHeaders ArrayList<ViaHeader> viaHeaders = this.sipProvider.getLocalViaHeaders(toAddress); // MaxForwards MaxForwardsHeader maxForwards = this.sipProvider.getMaxForwardsHeader(); // Content params ContentTypeHeader contTypeHeader; ContentLengthHeader contLengthHeader; try { contTypeHeader = this.sipProvider .getHeaderFactory() .createContentTypeHeader(getType(message), getSubType(message)); if (!DEFAULT_MIME_ENCODING.equalsIgnoreCase(message.getEncoding())) contTypeHeader.setParameter("charset", message.getEncoding()); contLengthHeader = this.sipProvider.getHeaderFactory().createContentLengthHeader(message.getSize()); } catch (ParseException ex) { // these two should never happen. logger.error("An unexpected error occurred while" + "constructing the content headers", ex); throw new OperationFailedException( "An unexpected error occurred while" + "constructing the content headers", OperationFailedException.INTERNAL_ERROR, ex); } catch (InvalidArgumentException exc) { // these two should never happen. logger.error( "An unexpected error occurred while" + "constructing the content length header", exc); throw new OperationFailedException( "An unexpected error occurred while" + "constructing the content length header", OperationFailedException.INTERNAL_ERROR, exc); } Request req; try { req = this.sipProvider .getMessageFactory() .createRequest( toHeader.getAddress().getURI(), Request.MESSAGE, callIdHeader, cSeqHeader, fromHeader, toHeader, viaHeaders, maxForwards, contTypeHeader, message.getRawData()); } catch (ParseException ex) { // shouldn't happen logger.error("Failed to create message Request!", ex); throw new OperationFailedException( "Failed to create message Request!", OperationFailedException.INTERNAL_ERROR, ex); } req.addHeader(contLengthHeader); return req; }
/** * Find the <tt>ProtocolProviderServiceSipImpl</tt> (one of our "candidate recipient" listeners) * which this <tt>request</tt> should be dispatched to. The strategy is to look first at the * request URI, and then at the To field to find a matching candidate for dispatching. Note that * this method takes a <tt>Request</tt> as param, and not a <tt>ServerTransaction</tt>, because * sometimes <tt>RequestEvent</tt>s have no associated <tt>ServerTransaction</tt>. * * @param request the <tt>Request</tt> to find a recipient for. * @return a suitable <tt>ProtocolProviderServiceSipImpl</tt>. */ private ProtocolProviderServiceSipImpl findTargetFor(Request request) { if (request == null) { logger.error("request shouldn't be null."); return null; } List<ProtocolProviderServiceSipImpl> currentListenersCopy = new ArrayList<ProtocolProviderServiceSipImpl>(this.getSipListeners()); // Let's first narrow down candidate choice by comparing // addresses and ports (no point in delivering to a provider with a // non matching IP address since they will reject it anyway). filterByAddress(currentListenersCopy, request); if (currentListenersCopy.size() == 0) { logger.error("no listeners"); return null; } URI requestURI = request.getRequestURI(); if (requestURI.isSipURI()) { String requestUser = ((SipURI) requestURI).getUser(); List<ProtocolProviderServiceSipImpl> candidates = new ArrayList<ProtocolProviderServiceSipImpl>(); // check if the Request-URI username is // one of ours usernames for (ProtocolProviderServiceSipImpl listener : currentListenersCopy) { String ourUserID = listener.getAccountID().getUserID(); // logger.trace(ourUserID + " *** " + requestUser); if (ourUserID.equals(requestUser)) { if (logger.isTraceEnabled()) logger.trace("suitable candidate found: " + listener.getAccountID()); candidates.add(listener); } } // the perfect match // every other case is approximation if (candidates.size() == 1) { ProtocolProviderServiceSipImpl perfectMatch = candidates.get(0); if (logger.isTraceEnabled()) logger.trace("Will dispatch to \"" + perfectMatch.getAccountID() + "\""); return perfectMatch; } // more than one account match if (candidates.size() > 1) { // check if a custom param exists in the contact // address (set for registrar accounts) for (ProtocolProviderServiceSipImpl candidate : candidates) { String hostValue = ((SipURI) requestURI).getParameter(SipStackSharing.CONTACT_ADDRESS_CUSTOM_PARAM_NAME); if (hostValue == null) continue; if (hostValue.equals(candidate.getContactAddressCustomParamValue())) { if (logger.isTraceEnabled()) logger.trace( "Will dispatch to \"" + candidate.getAccountID() + "\" because " + "\" the custom param was set"); return candidate; } } // Past this point, our guess is not reliable. We try to find // the "least worst" match based on parameters like the To field // check if the To header field host part // matches any of our SIP hosts for (ProtocolProviderServiceSipImpl candidate : candidates) { URI fromURI = ((FromHeader) request.getHeader(FromHeader.NAME)).getAddress().getURI(); if (fromURI.isSipURI() == false) continue; SipURI ourURI = (SipURI) candidate.getOurSipAddress((SipURI) fromURI).getURI(); String ourHost = ourURI.getHost(); URI toURI = ((ToHeader) request.getHeader(ToHeader.NAME)).getAddress().getURI(); if (toURI.isSipURI() == false) continue; String toHost = ((SipURI) toURI).getHost(); // logger.trace(toHost + "***" + ourHost); if (toHost.equals(ourHost)) { if (logger.isTraceEnabled()) logger.trace( "Will dispatch to \"" + candidate.getAccountID() + "\" because " + "host in the To: is the same as in our AOR"); return candidate; } } // fallback on the first candidate ProtocolProviderServiceSipImpl target = candidates.iterator().next(); logger.info( "Will randomly dispatch to \"" + target.getAccountID() + "\" because there is ambiguity on the username from" + " the Request-URI"); if (logger.isTraceEnabled()) logger.trace("\n" + request); return target; } // fallback on any account ProtocolProviderServiceSipImpl target = currentListenersCopy.iterator().next(); if (logger.isDebugEnabled()) logger.debug( "Will randomly dispatch to \"" + target.getAccountID() + "\" because the username in the Request-URI " + "is unknown or empty"); if (logger.isTraceEnabled()) logger.trace("\n" + request); return target; } else { logger.error("Request-URI is not a SIP URI, dropping"); } return null; }
/** * Dispatches the event received from a JAIN-SIP <tt>SipProvider</tt> to one of our "candidate * recipient" listeners. * * @param event the event received for a <tt>SipProvider</tt>. */ public void processRequest(RequestEvent event) { try { Request request = event.getRequest(); if (logger.isTraceEnabled()) logger.trace("received request: " + request.getMethod()); /* * Create the transaction if it doesn't exist yet. If it is a * dialog-creating request, the dialog will also be automatically * created by the stack. */ if (event.getServerTransaction() == null) { try { // apply some hacks if needed on incoming request // to be compliant with some servers/clients // if needed stop further processing. if (applyNonConformanceHacks(event)) return; SipProvider source = (SipProvider) event.getSource(); ServerTransaction transaction = source.getNewServerTransaction(request); /* * Update the event, otherwise getServerTransaction() and * getDialog() will still return their previous value. */ event = new RequestEvent(source, transaction, transaction.getDialog(), request); } catch (SipException ex) { logger.error( "couldn't create transaction, please report " + "this to [email protected]", ex); } } ProtocolProviderServiceSipImpl service = getServiceData(event.getServerTransaction()); if (service != null) { service.processRequest(event); } else { service = findTargetFor(request); if (service == null) { logger.error("couldn't find a ProtocolProviderServiceSipImpl " + "to dispatch to"); if (event.getServerTransaction() != null) event.getServerTransaction().terminate(); } else { /* * Mark the dialog for the dispatching of later in-dialog * requests. If there is no dialog, we need to mark the * request to dispatch a possible timeout when sending the * response. */ Object container = event.getDialog(); if (container == null) container = request; SipApplicationData.setApplicationData(container, SipApplicationData.KEY_SERVICE, service); service.processRequest(event); } } } catch (Throwable exc) { /* * Any exception thrown within our code should be caught here so * that we could log it rather than interrupt stack activity with * it. */ this.logApplicationException(DialogTerminatedEvent.class, exc); // Unfortunately, death can hardly be ignored. if (exc instanceof ThreadDeath) throw (ThreadDeath) exc; } }