/**
   * Subscribes this provider as interested in receiving notifications for new mail messages from
   * Google mail services such as Gmail or Google Apps.
   */
  private void subscribeForGmailNotifications() {
    // first check support for the notification service
    String accountIDService = jabberProvider.getAccountID().getService();
    boolean notificationsAreSupported =
        jabberProvider.isFeatureSupported(accountIDService, NewMailNotificationIQ.NAMESPACE);

    if (!notificationsAreSupported) {
      if (logger.isDebugEnabled())
        logger.debug(
            accountIDService
                + " does not seem to provide a Gmail notification "
                + " service so we won't be trying to subscribe for it");
      return;
    }

    if (logger.isDebugEnabled())
      logger.debug(
          accountIDService
              + " seems to provide a Gmail notification "
              + " service so we will try to subscribe for it");

    ProviderManager providerManager = ProviderManager.getInstance();

    providerManager.addIQProvider(
        MailboxIQ.ELEMENT_NAME, MailboxIQ.NAMESPACE, new MailboxIQProvider());
    providerManager.addIQProvider(
        NewMailNotificationIQ.ELEMENT_NAME,
        NewMailNotificationIQ.NAMESPACE,
        new NewMailNotificationProvider());

    Connection connection = jabberProvider.getConnection();

    connection.addPacketListener(new MailboxIQListener(), new PacketTypeFilter(MailboxIQ.class));
    connection.addPacketListener(
        new NewMailNotificationListener(), new PacketTypeFilter(NewMailNotificationIQ.class));

    if (opSetPersPresence.getCurrentStatusMessage().equals(JabberStatusEnum.OFFLINE)) return;

    // create a query with -1 values for newer-than-tid and
    // newer-than-time attributes
    MailboxQueryIQ mailboxQuery = new MailboxQueryIQ();

    if (logger.isTraceEnabled())
      logger.trace(
          "sending mailNotification for acc: "
              + jabberProvider.getAccountID().getAccountUniqueID());
    jabberProvider.getConnection().sendPacket(mailboxQuery);
  }
示例#2
0
  /**
   * 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);
    }
  }
示例#3
0
  /**
   * Sets the icon for the given file.
   *
   * @param file the file to set an icon for
   * @return the byte array containing the thumbnail
   */
  private byte[] getFileThumbnail(File file) {
    byte[] bytes = null;
    if (FileUtils.isImage(file.getName())) {
      try {
        ImageIcon image = new ImageIcon(file.toURI().toURL());
        int width = image.getIconWidth();
        int height = image.getIconHeight();

        if (width > THUMBNAIL_WIDTH) width = THUMBNAIL_WIDTH;
        if (height > THUMBNAIL_HEIGHT) height = THUMBNAIL_HEIGHT;

        bytes = ImageUtils.getScaledInstanceInBytes(image.getImage(), width, height);
      } catch (MalformedURLException e) {
        if (logger.isDebugEnabled()) logger.debug("Could not locate image.", e);
      }
    }
    return bytes;
  }
  /**
   * Sends a file transfer request to the given <tt>toContact</tt> by specifying the local and
   * remote file path and the <tt>fromContact</tt>, sending the file.
   *
   * @param toContact the contact that should receive the file
   * @param file the file to send
   * @return the transfer object
   * @throws IllegalStateException if the protocol provider is not registered or connected
   * @throws IllegalArgumentException if some of the arguments doesn't fit the protocol requirements
   */
  public FileTransfer sendFile(Contact toContact, File file)
      throws IllegalStateException, IllegalArgumentException {
    assertConnected();

    if (file.length() > getMaximumFileLength())
      throw new IllegalArgumentException("File length exceeds the allowed one for this protocol");

    // Get the aim connection
    AimConnection aimConnection = icqProvider.getAimConnection();

    // Create an outgoing file transfer instance
    OutgoingFileTransfer outgoingFileTransfer =
        aimConnection
            .getIcbmService()
            .getRvConnectionManager()
            .createOutgoingFileTransfer(new Screenname(toContact.getAddress()));

    String id =
        String.valueOf(outgoingFileTransfer.getRvSessionInfo().getRvSession().getRvSessionId());

    FileTransferImpl outFileTransfer =
        new FileTransferImpl(outgoingFileTransfer, id, toContact, file, FileTransfer.OUT);

    // Adding the file to the outgoing file transfer
    try {
      outgoingFileTransfer.setSingleFile(new File(file.getPath()));
    } catch (IOException e) {
      if (logger.isDebugEnabled()) logger.debug("Error sending file", e);
      return null;
    }

    // Notify all interested listeners that a file transfer has been
    // created.
    FileTransferCreatedEvent event = new FileTransferCreatedEvent(outFileTransfer, new Date());

    fireFileTransferCreated(event);

    // Sending the file
    outgoingFileTransfer.sendRequest(new InvitationMessage(""));

    outFileTransfer.fireStatusChangeEvent(FileTransferStatusChangeEvent.PREPARING);

    return outFileTransfer;
  }
  /**
   * Sends the <tt>message</tt> to the destination indicated by the <tt>to</tt> contact.
   *
   * @param to the <tt>Contact</tt> to send <tt>message</tt> to
   * @param message the <tt>Message</tt> to send.
   * @throws java.lang.IllegalStateException if the underlying stack is not registered and
   *     initialized.
   * @throws java.lang.IllegalArgumentException if <tt>to</tt> is not an instance of ContactImpl.
   */
  public void sendInstantMessage(Contact to, Message message)
      throws IllegalStateException, IllegalArgumentException {
    if (!(to instanceof ContactSipImpl))
      throw new IllegalArgumentException("The specified contact is not a Sip contact." + to);

    assertConnected();

    // offline message
    if (to.getPresenceStatus().equals(sipStatusEnum.getStatus(SipStatusEnum.OFFLINE))
        && !offlineMessageSupported) {
      if (logger.isDebugEnabled()) logger.debug("trying to send a message to an offline contact");
      fireMessageDeliveryFailed(
          message, to, MessageDeliveryFailedEvent.OFFLINE_MESSAGES_NOT_SUPPORTED);
      return;
    }

    // create the message
    Request mes;
    try {
      mes = createMessageRequest(to, message);
    } catch (OperationFailedException ex) {
      logger.error("Failed to create the message.", ex);

      fireMessageDeliveryFailed(message, to, MessageDeliveryFailedEvent.INTERNAL_ERROR);
      return;
    }

    try {
      sendMessageRequest(mes, to, message);
    } catch (TransactionUnavailableException ex) {
      logger.error(
          "Failed to create messageTransaction.\n"
              + "This is most probably a network connection error.",
          ex);

      fireMessageDeliveryFailed(message, to, MessageDeliveryFailedEvent.NETWORK_FAILURE);
      return;
    } catch (SipException ex) {
      logger.error("Failed to send the message.", ex);

      fireMessageDeliveryFailed(message, to, MessageDeliveryFailedEvent.INTERNAL_ERROR);
      return;
    }
  }
  /**
   * Notify all listeners of the corresponding account detail change event.
   *
   * @param source the protocol provider service source
   * @param eventID the int ID of the event to dispatch
   * @param oldValue the value that the changed property had before the change occurred.
   * @param newValue the value that the changed property currently has (after the change has
   *     occurred).
   */
  public void fireServerStoredDetailsChangeEvent(
      ProtocolProviderService source, int eventID, Object oldValue, Object newValue) {
    ServerStoredDetailsChangeEvent evt =
        new ServerStoredDetailsChangeEvent(source, eventID, oldValue, newValue);

    Collection<ServerStoredDetailsChangeListener> listeners;
    synchronized (serverStoredDetailsListeners) {
      listeners = new ArrayList<ServerStoredDetailsChangeListener>(serverStoredDetailsListeners);
    }

    if (logger.isDebugEnabled())
      logger.debug(
          "Dispatching a Contact Property Change Event to"
              + listeners.size()
              + " listeners. Evt="
              + evt);

    for (ServerStoredDetailsChangeListener listener : listeners)
      listener.serverStoredDetailsChanged(evt);
  }
  /**
   * Handles buttons action events.
   *
   * @param evt the <tt>ActionEvent</tt> that notified us
   */
  public void actionPerformed(ActionEvent evt) {
    JButton sourceButton = (JButton) evt.getSource();

    if (sourceButton.equals(openFileButton)) {
      this.openFile(downloadFile);
    } else if (sourceButton.equals(openFolderButton)) {
      try {
        File downloadDir = GuiActivator.getFileAccessService().getDefaultDownloadDirectory();

        GuiActivator.getDesktopService().open(downloadDir);
      } catch (IllegalArgumentException e) {
        if (logger.isDebugEnabled()) logger.debug("Unable to open folder.", e);

        this.showErrorMessage(resources.getI18NString("service.gui.FOLDER_DOES_NOT_EXIST"));
      } catch (NullPointerException e) {
        if (logger.isDebugEnabled()) logger.debug("Unable to open folder.", e);

        this.showErrorMessage(resources.getI18NString("service.gui.FOLDER_DOES_NOT_EXIST"));
      } catch (UnsupportedOperationException e) {
        if (logger.isDebugEnabled()) logger.debug("Unable to open folder.", e);

        this.showErrorMessage(resources.getI18NString("service.gui.FILE_OPEN_NOT_SUPPORTED"));
      } catch (SecurityException e) {
        if (logger.isDebugEnabled()) logger.debug("Unable to open folder.", e);

        this.showErrorMessage(resources.getI18NString("service.gui.FOLDER_OPEN_NO_PERMISSION"));
      } catch (IOException e) {
        if (logger.isDebugEnabled()) logger.debug("Unable to open folder.", e);

        this.showErrorMessage(resources.getI18NString("service.gui.FOLDER_OPEN_NO_APPLICATION"));
      } catch (Exception e) {
        if (logger.isDebugEnabled()) logger.debug("Unable to open file.", e);

        this.showErrorMessage(resources.getI18NString("service.gui.FOLDER_OPEN_FAILED"));
      }
    } else if (sourceButton.equals(cancelButton)) {
      if (fileTransfer != null) fileTransfer.cancel();
    }
  }
    /** Thread entry point. */
    @Override
    public void run() {
      int status;
      long progress;
      String statusReason = "";

      while (true) {
        try {
          Thread.sleep(10);

          status = parseJabberStatus(jabberTransfer.getStatus());
          progress = fileTransfer.getTransferedBytes();

          if (status == FileTransferStatusChangeEvent.FAILED
              || status == FileTransferStatusChangeEvent.COMPLETED
              || status == FileTransferStatusChangeEvent.CANCELED
              || status == FileTransferStatusChangeEvent.REFUSED) {
            if (fileTransfer instanceof OutgoingFileTransferJabberImpl) {
              ((OutgoingFileTransferJabberImpl) fileTransfer).removeThumbnailRequestListener();
            }

            // sometimes a filetransfer can be preparing
            // and than completed :
            // transfered in one iteration of current thread
            // so it won't go through intermediate state - inProgress
            // make sure this won't happen
            if (status == FileTransferStatusChangeEvent.COMPLETED
                && fileTransfer.getStatus() == FileTransferStatusChangeEvent.PREPARING) {
              fileTransfer.fireStatusChangeEvent(
                  FileTransferStatusChangeEvent.IN_PROGRESS, "Status changed");
              fileTransfer.fireProgressChangeEvent(System.currentTimeMillis(), progress);
            }

            break;
          }

          fileTransfer.fireStatusChangeEvent(status, "Status changed");
          fileTransfer.fireProgressChangeEvent(System.currentTimeMillis(), progress);
        } catch (InterruptedException e) {
          if (logger.isDebugEnabled()) logger.debug("Unable to sleep thread.", e);
        }
      }

      if (jabberTransfer.getError() != null) {
        logger.error(
            "An error occured while transfering file: " + jabberTransfer.getError().getMessage());
      }

      if (jabberTransfer.getException() != null) {
        logger.error(
            "An exception occured while transfering file: ", jabberTransfer.getException());

        if (jabberTransfer.getException() instanceof XMPPException) {
          XMPPError error = ((XMPPException) jabberTransfer.getException()).getXMPPError();
          if (error != null)
            if (error.getCode() == 406 || error.getCode() == 403)
              status = FileTransferStatusChangeEvent.REFUSED;
        }

        statusReason = jabberTransfer.getException().getMessage();
      }

      if (initialFileSize > 0
          && status == FileTransferStatusChangeEvent.COMPLETED
          && fileTransfer.getTransferedBytes() < initialFileSize) {
        status = FileTransferStatusChangeEvent.CANCELED;
      }

      fileTransfer.fireStatusChangeEvent(status, statusReason);
      fileTransfer.fireProgressChangeEvent(System.currentTimeMillis(), progress);
    }
  /**
   * Creates an html description of the specified mailbox.
   *
   * @param mailboxIQ the mailboxIQ that we are to describe.
   * @return an html description of <tt>mailboxIQ</tt>
   */
  private String createMailboxDescription(MailboxIQ mailboxIQ) {
    int threadCount = mailboxIQ.getThreadCount();

    String resourceHeaderKey =
        threadCount > 1 ? "service.gui.NEW_GMAIL_MANY_HEADER" : "service.gui.NEW_GMAIL_HEADER";

    String resourceFooterKey =
        threadCount > 1 ? "service.gui.NEW_GMAIL_MANY_FOOTER" : "service.gui.NEW_GMAIL_FOOTER";

    // FIXME Escape HTML!
    String newMailHeader =
        JabberActivator.getResources()
            .getI18NString(
                resourceHeaderKey,
                new String[] {
                  jabberProvider.getAccountID().getService(), // {0} - service name
                  mailboxIQ.getUrl(), // {1} - inbox URI
                  Integer.toString(threadCount) // {2} - thread count
                });

    StringBuilder message = new StringBuilder(newMailHeader);

    // we now start an html table for the threads.
    message.append("<table width=100% cellpadding=2 cellspacing=0 ");
    message.append("border=0 bgcolor=#e8eef7>");

    Iterator<MailThreadInfo> threads = mailboxIQ.threads();

    String maxThreadsStr =
        (String)
            JabberActivator.getConfigurationService()
                .getProperty(PNAME_MAX_GMAIL_THREADS_PER_NOTIFICATION);

    int maxThreads = 5;

    try {
      if (maxThreadsStr != null) maxThreads = Integer.parseInt(maxThreadsStr);
    } catch (NumberFormatException e) {
      if (logger.isDebugEnabled())
        logger.debug("Failed to parse max threads count: " + maxThreads + ". Going for default.");
    }

    // print a maximum of MAX_THREADS
    for (int i = 0; i < maxThreads && threads.hasNext(); i++) {
      message.append(threads.next().createHtmlDescription());
    }
    message.append("</table><br/>");

    if (threadCount > maxThreads) {
      String messageFooter =
          JabberActivator.getResources()
              .getI18NString(
                  resourceFooterKey,
                  new String[] {
                    mailboxIQ.getUrl(), // {0} - inbox URI
                    Integer.toString(threadCount - maxThreads) // {1} - thread count
                  });
      message.append(messageFooter);
    }

    return message.toString();
  }
示例#10
0
  /**
   * 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);
  }
示例#11
0
  /**
   * 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;
  }
示例#12
0
  /**
   * 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;
    }
  }