/**
   * Constructor.
   *
   * @param session Session object for this service
   * @param urlname URLName object to be used for this service
   */
  protected Service(Session session, URLName urlname) {
    this.session = session;
    debug = session.getDebug();
    url = urlname;

    /*
     * Initialize the URLName with default values.
     * The URLName will be updated when connect is called.
     */
    String protocol = null;
    String host = null;
    int port = -1;
    String user = null;
    String password = null;
    String file = null;

    // get whatever information we can from the URL
    // XXX - url should always be non-null here, Session
    //       passes it into the constructor
    if (url != null) {
      protocol = url.getProtocol();
      host = url.getHost();
      port = url.getPort();
      user = url.getUsername();
      password = url.getPassword();
      file = url.getFile();
    }

    // try to get protocol-specific default properties
    if (protocol != null) {
      if (host == null) host = session.getProperty("mail." + protocol + ".host");
      if (user == null) user = session.getProperty("mail." + protocol + ".user");
    }

    // try to get mail-wide default properties
    if (host == null) host = session.getProperty("mail.host");

    if (user == null) user = session.getProperty("mail.user");

    // try using the system username
    if (user == null) {
      try {
        user = System.getProperty("user.name");
      } catch (SecurityException sex) {
        // XXX - it's not worth creating a MailLogger just for this
        // logger.log(Level.CONFIG, "Can't get user.name property", sex);
      }
    }

    url = new URLName(protocol, host, port, file, user, password);

    // create or choose the appropriate event queue
    String scope = session.getProperties().getProperty("mail.event.scope", "folder");
    Executor executor = (Executor) session.getProperties().get("mail.event.executor");
    if (scope.equalsIgnoreCase("application")) q = EventQueue.getApplicationEventQueue(executor);
    else if (scope.equalsIgnoreCase("session")) q = session.getEventQueue();
    else // if (scope.equalsIgnoreCase("store") ||
      //     scope.equalsIgnoreCase("folder"))
      q = new EventQueue(executor);
  }
 /**
  * Add the event and vector of listeners to the queue to be delivered.
  *
  * @param event the event
  * @param vector the vector of listeners
  */
 protected void queueEvent(MailEvent event, Vector vector) {
   /*
    * Copy the vector in order to freeze the state of the set
    * of EventListeners the event should be delivered to prior
    * to delivery.  This ensures that any changes made to the
    * Vector from a target listener's method during the delivery
    * of this event will not take effect until after the event is
    * delivered.
    */
   Vector v = (Vector) vector.clone();
   q.enqueue(event, v);
 }
  /**
   * Notify all ConnectionListeners. Service implementations are expected to use this method to
   * broadcast connection events.
   *
   * <p>The provided default implementation queues the event into an internal event queue. An event
   * dispatcher thread dequeues events from the queue and dispatches them to the registered
   * ConnectionListeners. Note that the event dispatching occurs in a separate thread, thus avoiding
   * potential deadlock problems.
   *
   * @param type the ConnectionEvent type
   */
  protected void notifyConnectionListeners(int type) {
    /*
     * Don't bother queuing an event if there's no listeners.
     * Yes, listeners could be removed after checking, which
     * just makes this an expensive no-op.
     */
    if (connectionListeners.size() > 0) {
      ConnectionEvent e = new ConnectionEvent(this, type);
      queueEvent(e, connectionListeners);
    }

    /* Fix for broken JDK1.1.x Garbage collector :
     *  The 'conservative' GC in JDK1.1.x occasionally fails to
     *  garbage-collect Threads which are in the wait state.
     *  This would result in thread (and consequently memory) leaks.
     *
     * We attempt to fix this by sending a 'terminator' event
     * to the queue, after we've sent the CLOSED event. The
     * terminator event causes the event-dispatching thread to
     * self destruct.
     */
    if (type == ConnectionEvent.CLOSED) q.terminateQueue();
  }
 /** Stop the event dispatcher thread so the queue can be garbage collected. */
 protected void finalize() throws Throwable {
   super.finalize();
   q.terminateQueue();
 }