/** * 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); } }
/** * Logs exceptions that have occurred in the application while processing events originating from * the stack. * * @param eventClass the class of the jain-sip event that we were handling when the exception was * thrown. * @param exc the exception that we need to log. */ private void logApplicationException(Class<DialogTerminatedEvent> eventClass, Throwable exc) { String message = "An error occurred while processing event of type: " + eventClass.getName(); logger.error(message, exc); if (logger.isDebugEnabled()) logger.debug(message, exc); }
/** * 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; }
/** * 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; } }