/** * Put the JAIN-SIP stack in a state where it cannot receive any data and frees the network ports * used. That is to say remove JAIN-SIP <tt>ListeningPoint</tt>s and <tt>SipProvider</tt>s. */ @SuppressWarnings("unchecked") // jain-sip legacy code private void stopListening() { try { this.secureJainSipProvider.removeSipListener(this); this.stack.deleteSipProvider(this.secureJainSipProvider); this.secureJainSipProvider = null; this.clearJainSipProvider.removeSipListener(this); this.stack.deleteSipProvider(this.clearJainSipProvider); this.clearJainSipProvider = null; Iterator<ListeningPoint> it = this.stack.getListeningPoints(); Vector<ListeningPoint> lpointsToRemove = new Vector<ListeningPoint>(); while (it.hasNext()) { lpointsToRemove.add(it.next()); } it = lpointsToRemove.iterator(); while (it.hasNext()) { this.stack.deleteListeningPoint(it.next()); } this.stack.stop(); if (logger.isTraceEnabled()) logger.trace("stopped listening"); } catch (ObjectInUseException ex) { logger.fatal("Failed to stop listening", ex); } }
/** * Adds this <tt>listener</tt> as a candidate recipient for the dispatching of new messages * received from the JAIN-SIP <tt>SipProvider</tt>s. * * @param listener a new possible target for the dispatching process. * @throws OperationFailedException if creating one of the underlying <tt>SipProvider</tt>s fails * for whatever reason. */ public void addSipListener(ProtocolProviderServiceSipImpl listener) throws OperationFailedException { synchronized (this.listeners) { if (this.listeners.size() == 0) startListening(); this.listeners.add(listener); if (logger.isTraceEnabled()) logger.trace(this.listeners.size() + " listeners now"); } }
/** * This <tt>listener</tt> will no longer be a candidate recipient for the dispatching of new * messages received from the JAIN-SIP <tt>SipProvider</tt>s. * * @param listener possible target to remove for the dispatching process. */ public void removeSipListener(ProtocolProviderServiceSipImpl listener) { synchronized (this.listeners) { this.listeners.remove(listener); int listenerCount = listeners.size(); if (logger.isTraceEnabled()) logger.trace(listenerCount + " listeners left"); if (listenerCount == 0) stopListening(); } }
/** * Retrieves and returns that ProtocolProviderService that this transaction belongs to, or * <tt>null</tt> if we couldn't associate it with a provider based on neither the request nor the * transaction itself. * * @param transaction the transaction that we'd like to determine a provider for. * @return a reference to the <tt>ProtocolProviderServiceSipImpl</tt> that <tt>transaction</tt> * was associated with or <tt>null</tt> if we couldn't determine which one it is. */ private ProtocolProviderServiceSipImpl getServiceData(Transaction transaction) { ProtocolProviderServiceSipImpl service = (ProtocolProviderServiceSipImpl) SipApplicationData.getApplicationData( transaction.getRequest(), SipApplicationData.KEY_SERVICE); if (service != null) { if (logger.isTraceEnabled()) logger.trace("service was found in request data"); return service; } service = (ProtocolProviderServiceSipImpl) SipApplicationData.getApplicationData( transaction.getDialog(), SipApplicationData.KEY_SERVICE); if (service != null) { if (logger.isTraceEnabled()) logger.trace("service was found in dialog data"); } return service; }
/** * 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 processIOException(IOExceptionEvent event) { try { if (logger.isTraceEnabled()) logger.trace(event); // impossible to dispatch, log here if (logger.isDebugEnabled()) logger.debug("@todo implement processIOException()"); } 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); } }
/** * Receives options requests and replies with an OK response containing methods that we support. * * @param requestEvent the incoming options request. * @return <tt>true</tt> if request has been successfully processed, <tt>false</tt> otherwise */ @Override public boolean processRequest(RequestEvent requestEvent) { Response optionsOK = null; try { optionsOK = provider.getMessageFactory().createResponse(Response.OK, requestEvent.getRequest()); // add to the allows header all methods that we support for (String method : provider.getSupportedMethods()) { // don't support REGISTERs if (!method.equals(Request.REGISTER)) optionsOK.addHeader(provider.getHeaderFactory().createAllowHeader(method)); } Iterable<String> knownEventsList = provider.getKnownEventsList(); synchronized (knownEventsList) { for (String event : knownEventsList) optionsOK.addHeader(provider.getHeaderFactory().createAllowEventsHeader(event)); } } catch (ParseException ex) { // What else could we do apart from logging? logger.warn("Failed to create an incoming OPTIONS request", ex); return false; } try { SipStackSharing.getOrCreateServerTransaction(requestEvent).sendResponse(optionsOK); } catch (TransactionUnavailableException ex) { // this means that we received an OPTIONS request outside the scope // of a transaction which could mean that someone is simply sending // us b****hit to keep a NAT connection alive, so let's not get too // excited. if (logger.isInfoEnabled()) logger.info("Failed to respond to an incoming " + "transactionless OPTIONS request"); if (logger.isTraceEnabled()) logger.trace("Exception was:", ex); return false; } catch (InvalidArgumentException ex) { // What else could we do apart from logging? logger.warn("Failed to send an incoming OPTIONS request", ex); return false; } catch (SipException ex) { // What else could we do apart from logging? logger.warn("Failed to send an incoming OPTIONS request", ex); return false; } return true; }
/** * Put the stack in a state where it can receive data on three UDP/TCP ports (2 for clear * communication, 1 for TLS). That is to say create the related JAIN-SIP <tt>ListeningPoint</tt>s * and <tt>SipProvider</tt>s. * * @throws OperationFailedException if creating one of the underlying <tt>SipProvider</tt>s fails * for whatever reason. */ private void startListening() throws OperationFailedException { try { int bindRetriesValue = getBindRetriesValue(); this.createProvider(this.getPreferredClearPort(), bindRetriesValue, false); this.createProvider(this.getPreferredSecurePort(), bindRetriesValue, true); this.stack.start(); if (logger.isTraceEnabled()) logger.trace("started listening"); } catch (Exception ex) { logger.error( "An unexpected error happened while creating the" + "SipProviders and ListeningPoints."); throw new OperationFailedException( "An unexpected error hapenned" + "while initializing the SIP stack", OperationFailedException.INTERNAL_ERROR, ex); } }
/** * The method queries a Stun server for a binding for the port and address that <tt>sock</tt> is * bound on. * * @param sock the socket whose port and address we'dlike to resolve (the stun message gets sent * trhough that socket) * @return StunAddress the address returned by the stun server or null if an error occurred or no * address was returned * @throws IOException if an error occurs while stun4j is using sockets. * @throws BindException if the port is already in use. */ private StunAddress queryStunServer(DatagramSocket sock) throws IOException, BindException { StunAddress mappedAddress = null; if (detector != null && useStun) { mappedAddress = detector.getMappingFor(sock); if (logger.isTraceEnabled()) { logger.trace( "For socket with address " + sock.getLocalAddress().getHostAddress() + " and port " + sock.getLocalPort() + " the stun server returned the " + "following mapping [" + mappedAddress + "]"); } } return mappedAddress; }
/** * Function called when a icq file transfer request arrive * * @param manager the joustsim manager * @param transfer the incoming transfer */ public void handleNewIncomingConnection( RvConnectionManager manager, IncomingRvConnection transfer) { if (transfer instanceof IncomingFileTransfer) { if (logger.isTraceEnabled()) logger.trace("Incoming Icq file transfer request " + transfer.getClass()); if (!(transfer instanceof IncomingFileTransfer)) { logger.warn("Wrong file transfer."); return; } OperationSetPersistentPresenceIcqImpl opSetPersPresence = (OperationSetPersistentPresenceIcqImpl) icqProvider.getOperationSet(OperationSetPersistentPresence.class); Contact sender = opSetPersPresence.findContactByID(transfer.getBuddyScreenname().getFormatted()); IncomingFileTransfer incomingFileTransfer = (IncomingFileTransfer) transfer; final Date newDate = new Date(); final IncomingFileTransferRequest req = new IncomingFileTransferRequestIcqImpl( icqProvider, this, incomingFileTransfer, sender, newDate); // this handels when we receive request and before accept or decline // it we receive cancel transfer.addEventListener( new RvConnectionEventListener() { public void handleEventWithStateChange( RvConnection transfer, RvConnectionState state, RvConnectionEvent event) { if (state == FileTransferState.FAILED && event instanceof BuddyCancelledEvent) { fireFileTransferRequestCanceled( new FileTransferRequestEvent( OperationSetFileTransferIcqImpl.this, req, newDate)); } } public void handleEvent(RvConnection arg0, RvConnectionEvent arg1) {} }); fireFileTransferRequest(new FileTransferRequestEvent(this, req, newDate)); } }
/** * 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 processResponse(ResponseEvent event) { try { // we don't have to accept the transaction since we // created the request ClientTransaction transaction = event.getClientTransaction(); if (logger.isTraceEnabled()) logger.trace( "received response: " + event.getResponse().getStatusCode() + " " + event.getResponse().getReasonPhrase()); if (transaction == null) { logger.warn("Transaction is null, probably already expired!"); return; } ProtocolProviderServiceSipImpl service = getServiceData(transaction); if (service != null) { // Mark the dialog for the dispatching of later in-dialog // responses. If there is no dialog then the initial request // sure is marked otherwise we won't have found the service with // getServiceData(). The request has to be marked in case we // receive one more response in an out-of-dialog transaction. if (event.getDialog() != null) { SipApplicationData.setApplicationData( event.getDialog(), SipApplicationData.KEY_SERVICE, service); } service.processResponse(event); } else { logger.error( "We received a response which " + "wasn't marked, please report this to " + "*****@*****.**"); } } 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); } }
/** * 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 processDialogTerminated(DialogTerminatedEvent event) { try { ProtocolProviderServiceSipImpl recipient = (ProtocolProviderServiceSipImpl) SipApplicationData.getApplicationData( event.getDialog(), SipApplicationData.KEY_SERVICE); if (recipient == null) { logger.error( "Dialog wasn't marked, please report this to " + "*****@*****.**"); } else { if (logger.isTraceEnabled()) logger.trace("service was found with dialog data"); recipient.processDialogTerminated(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); } }
/** * Constructor for this class. Creates the JAIN-SIP stack. * * @throws OperationFailedException if creating the stack fails. */ SipStackSharing() throws OperationFailedException { // init of the stack try { SipFactory sipFactory = SipFactory.getInstance(); sipFactory.setPathName("org.jitsi.gov.nist"); Properties sipStackProperties = new SipStackProperties(); // Create SipStack object this.stack = sipFactory.createSipStack(sipStackProperties); if (logger.isTraceEnabled()) logger.trace("Created stack: " + this.stack); // set our custom address resolver managing SRV records AddressResolverImpl addressResolver = new AddressResolverImpl(); ((SIPTransactionStack) this.stack).setAddressResolver(addressResolver); SipActivator.getNetworkAddressManagerService().addNetworkConfigurationChangeListener(this); } catch (Exception ex) { logger.fatal("Failed to get SIP Factory.", ex); throw new OperationFailedException( "Failed to get SIP Factory", OperationFailedException.INTERNAL_ERROR, ex); } }
/** * Logs the build time stamp of the jain-sip reference implementation. * * @param buildTimeStamp the build time stamp of the jain-sip reference implementation. */ public void setBuildTimeStamp(String buildTimeStamp) { if (logger.isTraceEnabled()) logger.trace("JAIN-SIP RI build " + buildTimeStamp); }
/** * Notifies this instance that a <tt>DatagramPacket</tt> packet received on the data * <tt>DatagramSocket</tt> of this <tt>Channel</tt> has been accepted for further processing * within Jitsi Videobridge. * * @param pkt the accepted <tt>RawPacket</tt>. */ public void accepted(RawPacket pkt) { // With native simulcast we don't have a notification when a stream // has started/stopped. The simulcast manager implements a timeout // for the high quality stream and it needs to be notified when // the channel has accepted a datagram packet for the timeout to // function correctly. if (!hasLayers() || pkt == null) { return; } // Find the layer that corresponds to this packet. int acceptedSSRC = pkt.getSSRC(); SimulcastLayer[] layers = getSimulcastLayers(); SimulcastLayer acceptedLayer = null; for (SimulcastLayer layer : layers) { // We only care about the primary SSRC and not the RTX ssrc (or // future FEC ssrc). if ((int) layer.getPrimarySSRC() == acceptedSSRC) { acceptedLayer = layer; break; } } // If this is not an RTP packet or if we can't find an accepted // layer, log and return as it makes no sense to continue in this // situation. if (acceptedLayer == null) { return; } // There are sequences of packets with increasing timestamps but without // the marker bit set. Supposedly, they are probes to detect whether the // bandwidth may increase. We think that they should cause neither the // start nor the stop of any SimulcastLayer. // XXX There's RawPacket#getPayloadLength() but the implementation // includes pkt.paddingSize at the time of this writing and we do not // know whether that's going to stay that way. int pktPayloadLength = pkt.getLength() - pkt.getHeaderLength(); int pktPaddingSize = pkt.getPaddingSize(); if (pktPayloadLength <= pktPaddingSize) { if (logger.isTraceEnabled()) { logger.trace( "pkt.payloadLength= " + pktPayloadLength + " <= pkt.paddingSize= " + pktPaddingSize + "(" + pkt.getSequenceNumber() + ")"); } return; } // NOTE(gp) we expect the base layer to be always on, so we never touch // it or starve it. // XXX Refer to the implementation of // SimulcastLayer#touch(boolean, RawPacket) for an explanation of why we // chose to use a return value. boolean frameStarted = acceptedLayer.touch(pkt); if (frameStarted) simulcastLayerFrameStarted(acceptedLayer, pkt, layers); }
/** * Log an error message. * * @param message -- error message to log. */ public void logFatalError(String message) { if (logger.isTraceEnabled()) logger.trace("Fatal error from the JAIN-SIP stack: " + message); }
/** * Attach JAIN-SIP <tt>SipProvider</tt> and <tt>ListeningPoint</tt> to the stack either for clear * communications or TLS. Clear UDP and TCP <tt>ListeningPoint</tt>s are not handled separately as * the former is a fallback for the latter (depending on the size of the data transmitted). Both * <tt>ListeningPoint</tt>s must be bound to the same address and port in order for the related * <tt>SipProvider</tt> to be created. If a UDP or TCP <tt>ListeningPoint</tt> cannot bind, retry * for both on another port. * * @param preferredPort which port to try first to bind. * @param retries how many times should we try to find a free port to bind * @param secure whether to create the TLS SipProvider. or the clear UDP/TCP one. * @throws TransportNotSupportedException in case we try to create a provider for a transport not * currently supported by jain-sip * @throws InvalidArgumentException if we try binding to an illegal port (which we won't) * @throws ObjectInUseException if another <tt>SipProvider</tt> is already associated with this * <tt>ListeningPoint</tt>. * @throws TransportAlreadySupportedException if there is already a ListeningPoint associated to * this <tt>SipProvider</tt> with the same transport of the <tt>ListeningPoint</tt>. * @throws TooManyListenersException if we try to add a new <tt>SipListener</tt> with a * <tt>SipProvider</tt> when one was already registered. */ private void createProvider(int preferredPort, int retries, boolean secure) throws TransportNotSupportedException, InvalidArgumentException, ObjectInUseException, TransportAlreadySupportedException, TooManyListenersException { String context = (secure ? "TLS: " : "clear UDP/TCP: "); if (retries < 0) { // very unlikely to happen with the default 50 retries logger.error(context + "couldn't find free ports to listen on."); return; } ListeningPoint tlsLP = null; ListeningPoint udpLP = null; ListeningPoint tcpLP = null; try { if (secure) { tlsLP = this.stack.createListeningPoint( NetworkUtils.IN_ADDR_ANY, preferredPort, ListeningPoint.TLS); if (logger.isTraceEnabled()) logger.trace("TLS secure ListeningPoint has been created."); this.secureJainSipProvider = this.stack.createSipProvider(tlsLP); this.secureJainSipProvider.addSipListener(this); } else { udpLP = this.stack.createListeningPoint( NetworkUtils.IN_ADDR_ANY, preferredPort, ListeningPoint.UDP); tcpLP = this.stack.createListeningPoint( NetworkUtils.IN_ADDR_ANY, preferredPort, ListeningPoint.TCP); if (logger.isTraceEnabled()) logger.trace("UDP and TCP clear ListeningPoints have " + "been created."); this.clearJainSipProvider = this.stack.createSipProvider(udpLP); this.clearJainSipProvider.addListeningPoint(tcpLP); this.clearJainSipProvider.addSipListener(this); } if (logger.isTraceEnabled()) logger.trace(context + "SipProvider has been created."); } catch (InvalidArgumentException ex) { // makes sure we didn't leave an open listener // as both UDP and TCP listener have to bind to the same port if (tlsLP != null) this.stack.deleteListeningPoint(tlsLP); if (udpLP != null) this.stack.deleteListeningPoint(udpLP); if (tcpLP != null) this.stack.deleteListeningPoint(tcpLP); // FIXME: "Address already in use" is not working // as ex.getMessage() displays in the locale language in SC // (getMessage() is always supposed to be English though) // this should be a temporary workaround // if (ex.getMessage().indexOf("Address already in use") != -1) // another software is probably using the port if (ex.getCause() instanceof java.io.IOException) { if (logger.isDebugEnabled()) logger.debug("Port " + preferredPort + " seems in use for either TCP or UDP."); // tries again on a new random port int currentlyTriedPort = NetworkUtils.getRandomPortNumber(); if (logger.isDebugEnabled()) logger.debug("Retrying bind on port " + currentlyTriedPort); this.createProvider(currentlyTriedPort, retries - 1, secure); } else throw ex; } }
/** * 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; } }
/** * logs a stack trace. This helps to look at the stack frame. * * @param traceLevel currently unused. */ public void logStackTrace(int traceLevel) { if (logger.isTraceEnabled()) logger.trace("JAIN-SIP stack trace", new Throwable()); }