@Override
  public void release() {

    // delayedTasks.cancel();
    releaseListeners();
    super.release();
  }
  @Override
  public void stop() {
    this.releaseListeners();

    // when stopping connection manager we need to stop all active connections as well
    for (IO service : services.values()) {
      service.forceStop();
    }
    super.stop();
  }
  @Override
  public synchronized void everyMinute() {
    super.everyMinute();

    // This variable used to provide statistics gets off on a busy
    // services as it is handled in methods called concurrently by
    // many threads. While accuracy of this variable is not critical
    // for the server functions, statistics should be as accurate as
    // possible to provide valuable metrics data.
    // So in the watchdog thread we re-synchronize this number
    int tmp = services.size();

    services_size = tmp;
    doForAllServices(ioStatsGetter);
  }
  @Override
  public void initializationCompleted() {
    if (isInitializationComplete()) {

      // Do we really need to do this again?
      return;
    }
    super.initializationCompleted();
    initializationCompleted = true;
    for (Map<String, Object> params : waitingTasks) {
      reconnectService(params, connectionDelay);
    }
    waitingTasks.clear();
    if (null != watchdog) {
      watchdog.start();
    }
  }
  @Override
  public void getStatistics(StatisticsList list) {
    super.getStatistics(list);
    list.add(getName(), "Open connections", services_size, Level.INFO);
    if (list.checkLevel(Level.FINEST) || (services.size() < 1000)) {
      int waitingToSendSize = 0;

      for (IO serv : services.values()) {
        waitingToSendSize += serv.waitingToSendSize();
      }
      list.add(getName(), "Waiting to send", waitingToSendSize, Level.FINE);
    }
    list.add(getName(), "Bytes sent", bytesSent, Level.FINE);
    list.add(getName(), "Bytes received", bytesReceived, Level.FINE);
    list.add(getName(), "Socket overflow", socketOverflow, Level.FINE);
    list.add(getName(), "Watchdog runs", watchdogRuns, Level.FINER);
    list.add(getName(), "Watchdog tests", watchdogTests, Level.FINE);
    list.add(getName(), "Watchdog stopped", watchdogStopped, Level.FINE);
  }
  @Override
  public void setProperties(Map<String, Object> props) throws ConfigurationException {
    super.setProperties(props);
    if (props.get(MAX_INACTIVITY_TIME) != null) {
      maxInactivityTime = (Long) props.get(MAX_INACTIVITY_TIME) * SECOND;
    }
    if (props.get(WHITE_CHAR_ACK_PROP_KEY) != null) {
      white_char_ack = (Boolean) props.get(WHITE_CHAR_ACK_PROP_KEY);
    }
    if (props.get(XMPP_ACK_PROP_KEY) != null) {
      xmpp_ack = (Boolean) props.get(XMPP_ACK_PROP_KEY);
    }
    if (props.get(NET_BUFFER_PROP_KEY) != null) {
      net_buffer = (Integer) props.get(NET_BUFFER_PROP_KEY);
    }
    if (props.get(NET_BUFFER_LIMIT_PROP_KEY) != null) {
      net_buffer_limit = (Integer) props.get(NET_BUFFER_LIMIT_PROP_KEY);
    }
    if (props.get(TRAFFIC_THROTTLING_PROP_KEY) != null) {
      String[] tmp = ((String) props.get(TRAFFIC_THROTTLING_PROP_KEY)).split(",");

      for (String tmp_s : tmp) {
        String[] tmp_thr = tmp_s.split(":");

        if (tmp_thr[0].equalsIgnoreCase("xmpp")) {
          last_minute_packets_limit =
              DataTypes.parseNum(tmp_thr[1], Long.class, LAST_MINUTE_PACKETS_LIMIT_PROP_VAL);
          log.warning(getName() + " last_minute_packets_limit = " + last_minute_packets_limit);
          total_packets_limit =
              DataTypes.parseNum(tmp_thr[2], Long.class, TOTAL_PACKETS_LIMIT_PROP_VAL);
          log.warning(getName() + " total_packets_limit = " + total_packets_limit);
          if (tmp_thr[3].equalsIgnoreCase("disc")) {
            xmppLimitAction = LIMIT_ACTION.DISCONNECT;
          }
          if (tmp_thr[3].equalsIgnoreCase("drop")) {
            xmppLimitAction = LIMIT_ACTION.DROP_PACKETS;
          }
        }
        if (tmp_thr[0].equalsIgnoreCase("bin")) {
          last_minute_bin_limit =
              DataTypes.parseNum(tmp_thr[1], Long.class, LAST_MINUTE_BIN_LIMIT_PROP_VAL);
          log.warning(getName() + " last_minute_bin_limit = " + last_minute_bin_limit);
          total_bin_limit = DataTypes.parseNum(tmp_thr[2], Long.class, TOTAL_BIN_LIMIT_PROP_VAL);
          log.warning(getName() + " total_bin_limit = " + total_bin_limit);
        }
      }
    }

    if (props.get(ELEMENTS_NUMBER_LIMIT_PROP_KEY) != null) {
      elements_number_limit = (int) props.get(ELEMENTS_NUMBER_LIMIT_PROP_KEY);
    }

    if (props.get(WATCHDOG_DELAY) != null) {
      watchdogDelay = (long) props.get(WATCHDOG_DELAY);
    }
    if (props.get(WATCHDOG_TIMEOUT) != null) {
      watchdogTimeout = (long) props.get(WATCHDOG_TIMEOUT);
    }
    if (props.get(WATCHDOG_PING_TYPE_KEY) != null) {
      String value = String.valueOf(props.get(WATCHDOG_PING_TYPE_KEY));
      watchdogPingType = WATCHDOG_PING_TYPE.valueOf(value.toUpperCase());
    }

    if (props.size() == 1) {

      // If props.size() == 1, it means this is a single property update and
      // ConnectionManager does not support it yet.
      return;
    }
    if (isInitializationComplete()) {

      // Do we really need to do this again?
      // Looks like reconfiguration for the port is not working correctly anyway
      // so for now we do not want to do it.
      return;
    }
    releaseListeners();

    int[] ports = (int[]) props.get(PORTS_PROP_KEY);

    if (ports != null) {
      for (int i = 0; i < ports.length; i++) {
        Map<String, Object> port_props = new LinkedHashMap<String, Object>(20);

        for (Map.Entry<String, Object> entry : props.entrySet()) {
          if (entry.getKey().startsWith(PROP_KEY + ports[i])) {
            int idx = entry.getKey().lastIndexOf('/');
            String key = entry.getKey().substring(idx + 1);

            log.log(
                Level.CONFIG,
                "Adding port property key: {0}={1}",
                new Object[] {key, entry.getValue()});
            port_props.put(key, entry.getValue());
          } // end of if (entry.getKey().startsWith())
        } // end of for ()
        port_props.put(PORT_KEY, ports[i]);
        if (port_props.containsKey(PORT_TYPE_PROP_KEY)
            && !(port_props.get(PORT_TYPE_PROP_KEY) instanceof ConnectionType)) {
          Object val = port_props.get(PORT_TYPE_PROP_KEY);
          port_props.put(PORT_TYPE_PROP_KEY, ConnectionType.valueOf(val.toString()));
        }
        if (port_props.containsKey(PORT_SOCKET_PROP_KEY)
            && !(port_props.get(PORT_SOCKET_PROP_KEY) instanceof SocketType)) {
          Object val = port_props.get(PORT_SOCKET_PROP_KEY);
          port_props.put(PORT_SOCKET_PROP_KEY, SocketType.valueOf(val.toString()));
        }
        addWaitingTask(port_props);
        // reconnectService(port_props, startDelay);
      } // end of for (int i = 0; i < ports.length; i++)
    } // end of if (ports != null)
  }
 @Override
 public void setName(String name) {
   super.setName(name);
   setupWatchdogThread();
 }
 @Override
 public void initBindings(Bindings binds) {
   super.initBindings(binds);
   binds.put(CommandIfc.SERVICES_MAP, services);
 }