@Override
    public void timeOutExpired(Packet packet) {

      // If we still haven't received confirmation from the SM then
      // the packet either has been lost or the server is overloaded
      // In either case we disconnect the connection.
      log.warning("No response within time limit received for a packet: " + packet.toString());

      BoshSession session = getBoshSession(packet.getFrom());

      if (session != null) {
        log.fine("Closing session for timeout: " + session.getSid());
        session.close();
        if (log.isLoggable(Level.FINE)) {
          log.log(
              Level.FINE,
              "{0} : {1} ({2})",
              new Object[] {
                BOSH_OPERATION_TYPE.REMOVE, session.getSid(), "Closing session for timeout"
              });
        }
        sessions.remove(session.getSid());
      } else {
        log.info("Session does not exist for packet: " + packet.toString());
      }
    }
  @Override
  protected boolean writePacketToSocket(Packet packet) {
    BoshSession session = getBoshSession(packet.getTo());

    if (session != null) {
      synchronized (session) {
        Queue<Packet> out_results = new ArrayDeque<Packet>();

        session.processPacket(packet, out_results);
        addOutPackets(out_results, session);
      }

      return true;
    } else {
      log.info("Session does not exist for packet: " + packet.toString());

      return false;
    }
  }
  @Override
  protected JID changeDataReceiver(
      Packet packet, JID newAddress, String command_sessionId, XMPPIOService<Object> serv) {
    BoshSession session = getBoshSession(packet.getTo());

    if (session != null) {
      String sessionId = session.getSessionId();

      if (sessionId.equals(command_sessionId)) {
        JID old_receiver = session.getDataReceiver();

        session.setDataReceiver(newAddress);

        return old_receiver;
      } else {
        log.info("Incorrect session ID, ignoring data redirect for: " + newAddress);
      }
    }

    return null;
  }
  @Override
  public boolean addOutStreamClosed(Packet packet, BoshSession bs, boolean withTimeout) {
    packet.setPacketFrom(getFromAddress(bs.getSid().toString()));
    packet.setPacketTo(bs.getDataReceiver());
    packet.initVars(packet.getPacketFrom(), packet.getPacketTo());
    bs.close();
    if (log.isLoggable(Level.FINEST)) {
      log.log(
          Level.FINEST,
          "{0} : {1} ({2})",
          new Object[] {BOSH_OPERATION_TYPE.REMOVE, bs.getSid(), "Closing bosh session"});
    }

    sessions.remove(bs.getSid());

    if (withTimeout) {
      return addOutPacketWithTimeout(packet, stoppedHandler, 15l, TimeUnit.SECONDS);
    } else {
      return addOutPacket(packet);
    }
  }
  /**
   * Method adds packets to the output queue stamping it with the appropriate {@link BoshSession}
   * data
   *
   * @param out_results collection of {@link Packet} objects to be added to queue
   * @param bs related {@link BoshSession}
   */
  protected void addOutPackets(Queue<Packet> out_results, BoshSession bs) {
    for (Packet res : out_results) {
      res.setPacketFrom(getFromAddress(bs.getSid().toString()));
      res.setPacketTo(bs.getDataReceiver());
      if (res.getCommand() != null) {
        switch (res.getCommand()) {
          case STREAM_CLOSED:
          case GETFEATURES:
            res.initVars(res.getPacketFrom(), res.getPacketTo());

            break;

          default:

            // Do nothing...
        }
      }
      addOutPacket(res);
    }
    out_results.clear();
  }
  @Override
  public boolean serviceStopped(XMPPIOService<Object> xmppService) {
    BoshIOService service = (BoshIOService) xmppService;
    boolean result = super.serviceStopped(service);

    UUID sid = service.getSid();

    if (sid != null) {
      BoshSession bs = sessions.get(sid);

      if (bs != null) {
        if (log.isLoggable(Level.FINE)) {
          log.log(
              Level.FINE,
              "{0} : {1} ({2})",
              new Object[] {BOSH_OPERATION_TYPE.REMOVE, bs.getSid(), "Closing bosh session"});
        }

        bs.disconnected(service);
      }
    }
    return result;
  }
  @Override
  protected void processCommand(Packet packet) {
    BoshSession session = getBoshSession(packet.getTo());

    switch (packet.getCommand()) {
      case USER_LOGIN:
        String jid = Command.getFieldValue(packet, "user-jid");

        if (jid != null) {
          if (session != null) {
            try {
              BareJID fromJID = BareJID.bareJIDInstance(jid);
              BareJID hostJid = getSeeOtherHostForJID(fromJID, Phase.LOGIN);

              if (hostJid != null) {
                Element streamErrorElement =
                    see_other_host_strategy.getStreamError(
                        "urn:ietf:params:xml:ns:xmpp-streams", hostJid);
                Packet redirectPacket = Packet.packetInstance(streamErrorElement);

                redirectPacket.setPacketTo(packet.getTo());
                writePacketToSocket(redirectPacket);
                session.sendWaitingPackets();
                session.close();
                if (log.isLoggable(Level.FINE)) {
                  log.log(
                      Level.FINE,
                      "{0} : {1} ({2})",
                      new Object[] {
                        BOSH_OPERATION_TYPE.REMOVE, session.getSid(), "See other host"
                      });
                }
                sessions.remove(session.getSid());
              } else {
                session.setUserJid(jid);
              }
            } catch (TigaseStringprepException ex) {
              log.log(Level.SEVERE, "user JID violates RFC6122 (XMPP:Address Format): ", ex);
            }
          } else {
            if (log.isLoggable(Level.FINE)) {
              log.log(Level.FINE, "Missing XMPPIOService for USER_LOGIN command: {0}", packet);
            }
          }
        } else {
          log.log(Level.WARNING, "Missing user-jid for USER_LOGIN command: {0}", packet);
        }

        break;

      case CLOSE:
        if (session != null) {
          if (log.isLoggable(Level.FINER)) {
            log.log(Level.FINER, "Closing session for command CLOSE: {0}", session.getSid());
          }
          try {
            List<Element> err_el = packet.getElement().getChildrenStaticStr(Iq.IQ_COMMAND_PATH);

            if ((err_el != null) && (err_el.size() > 0)) {
              Element error = new Element("stream:error");

              error.addChild(err_el.get(0));

              Packet condition = Packet.packetInstance(error);

              condition.setPacketTo(packet.getTo());
              writePacketToSocket(condition);
              session.sendWaitingPackets();
              bosh_session_close_delay = 100;
            }
          } catch (TigaseStringprepException ex) {
            Logger.getLogger(BoshConnectionManager.class.getName()).log(Level.SEVERE, null, ex);
          }
          if (bosh_session_close_delay > 0) {
            try {
              Thread.sleep(bosh_session_close_delay);
            } catch (InterruptedException ex) {

              // Intentionally left blank
            }
          }
          session.close();
          if (log.isLoggable(Level.FINE)) {
            log.log(
                Level.FINE,
                "{0} : {1} ({2})",
                new Object[] {
                  BOSH_OPERATION_TYPE.REMOVE, session.getSid(), "Closing session for command CLOSE"
                });
          }
          sessions.remove(session.getSid());
        } else {
          if (log.isLoggable(Level.FINE)) {
            log.log(Level.FINE, "Session does not exist for packet: {0}", packet);
          }
        }

        break;

      case CHECK_USER_CONNECTION:
        if (session != null) {

          // It's ok, the session has been found, respond with OK.
          addOutPacket(packet.okResult((String) null, 0));
        } else {

          // Session is no longer active, respond with an error.
          try {
            addOutPacket(
                Authorization.ITEM_NOT_FOUND.getResponseMessage(packet, "Connection gone.", false));
          } catch (PacketErrorTypeException e) {

            // Hm, error already, ignoring...
            log.log(Level.INFO, "Error packet is not really expected here: {0}", packet);
          }
        }

        break;

      default:
        super.processCommand(packet);

        break;
    } // end of switch (pc.getCommand())
  }
 /**
  * Returns full jid of passed BoshSession instance
  *
  * @param bs {@link BoshSession} for which JID should be retrieved
  * @return JID address related to particular {@link BoshSession}
  */
 @Override
 public JID getJidForBoshSession(BoshSession bs) {
   return getFromAddress(bs.getSid().toString());
 }
  @Override
  public Queue<Packet> processSocketData(XMPPIOService<Object> srv) {
    BoshIOService serv = (BoshIOService) srv;
    Packet p = null;

    while ((p = serv.getReceivedPackets().poll()) != null) {
      Queue<Packet> out_results = new ArrayDeque<Packet>(2);
      BoshSession bs = null;
      String sid_str = null;

      synchronized (sessions) {
        if (log.isLoggable(Level.FINER)) {
          log.log(
              Level.FINER,
              "Processing packet: {0}, type: {1}",
              new Object[] {p.getElemName(), p.getType()});
        }
        if (log.isLoggable(Level.FINEST)) {
          log.log(Level.FINEST, "Processing socket data: {0}", p);
        }
        sid_str = p.getAttributeStaticStr(SID_ATTR);

        UUID sid = null;

        if (sid_str == null) {
          String hostname = p.getAttributeStaticStr(Packet.TO_ATT);

          if ((hostname != null) && isLocalDomain(hostname)) {
            if (!isAllowed(srv, hostname)) {
              if (log.isLoggable(Level.FINE)) {
                log.log(Level.FINE, "Policy violation. Closing connection: {0}", p);
              }
              try {
                serv.sendErrorAndStop(Authorization.NOT_ALLOWED, p, "Policy violation.");
              } catch (IOException e) {
                log.log(
                    Level.WARNING, "Problem sending invalid hostname error for sid =  " + sid, e);
              }
            } else {
              bs =
                  new BoshSession(
                      getDefVHostItem().getDomain(),
                      JID.jidInstanceNS(routings.computeRouting(hostname)),
                      this,
                      sendNodeHostname ? getDefHostName().getDomain() : null,
                      maxSessionWaitingPackets);
              sid = bs.getSid();
              sessions.put(sid, bs);

              if (log.isLoggable(Level.FINE)) {
                log.log(
                    Level.FINE,
                    "{0} : {1} ({2})",
                    new Object[] {BOSH_OPERATION_TYPE.CREATE, sid, "Socket bosh session"});
              }
            }
          } else {
            try {
              serv.sendErrorAndStop(Authorization.NOT_ALLOWED, p, "Invalid hostname.");
            } catch (IOException e) {
              log.log(Level.WARNING, "Problem sending invalid hostname error for sid =  " + sid, e);
            }
          }
        } else {
          try {
            sid = UUID.fromString(sid_str);
            bs = sessions.get(sid);
          } catch (IllegalArgumentException e) {
            log.log(
                Level.WARNING,
                "Problem processing socket data, sid =  "
                    + sid_str
                    + " does not conform to the UUID string representation.",
                e);
          }
        }
      }
      try {
        if (bs != null) {
          synchronized (bs) {
            if (sid_str == null) {
              bs.init(
                  p,
                  serv,
                  max_wait,
                  min_polling,
                  max_inactivity,
                  concurrent_requests,
                  hold_requests,
                  max_pause,
                  max_batch_size,
                  batch_queue_timeout,
                  out_results);
            } else {
              bs.processSocketPacket(p, serv, out_results);
            }
          }
        } else {
          if (log.isLoggable(Level.FINE)) {
            log.log(
                Level.FINE,
                "{0} : {1} ({2})",
                new Object[] {BOSH_OPERATION_TYPE.INVALID_SID, sid_str, "Invalid SID"});
          }
          serv.sendErrorAndStop(Authorization.ITEM_NOT_FOUND, p, "Invalid SID");
        }
        addOutPackets(out_results, bs);
      } catch (IOException e) {
        log.log(Level.WARNING, "Problem processing socket data for sid =  " + sid_str, e);
      }

      // addOutPackets(out_results);
    } // end of while ()

    return null;
  }
  protected Map<String, String> preBindSession(Map<String, String> attr) {
    String hostname = attr.get(TO_ATTR);

    Queue<Packet> out_results = new ArrayDeque<Packet>(2);

    BoshSession bs =
        new BoshSession(
            getDefVHostItem().getDomain(),
            JID.jidInstanceNS(routings.computeRouting(hostname)),
            this,
            sendNodeHostname ? getDefHostName().getDomain() : null,
            maxSessionWaitingPackets);

    String jid = attr.get(FROM_ATTR);
    String uuid = UUID.randomUUID().toString();
    JID userId = JID.jidInstanceNS(jid);
    if (null == userId.getResource()) {
      userId = userId.copyWithResourceNS(uuid);
      attr.put(FROM_ATTR, userId.toString());
      bs.setUserJid(jid);
    }
    long rid = (long) (Math.random() * 10000000);

    attr.put(RID_ATTR, Long.toString(rid));

    UUID sid = bs.getSid();
    sessions.put(sid, bs);
    if (log.isLoggable(Level.FINE)) {
      log.log(
          Level.FINE,
          "{0} : {1} ({2})",
          new Object[] {BOSH_OPERATION_TYPE.CREATE, bs.getSid(), "Pre-bind"});
    }

    attr.put(SID_ATTR, sid.toString());

    Packet p = null;
    try {
      Element el = new Element("body");
      el.setAttributes(attr);
      p = Packet.packetInstance(el);
    } catch (TigaseStringprepException ex) {
      Logger.getLogger(BoshConnectionManager.class.getName()).log(Level.SEVERE, null, ex);
    }
    bs.init(
        p,
        null,
        max_wait,
        min_polling,
        max_inactivity,
        concurrent_requests,
        hold_requests,
        max_pause,
        max_batch_size,
        batch_queue_timeout,
        out_results,
        true);
    addOutPackets(out_results, bs);

    attr.put("hostname", getDefHostName().toString());

    return attr;
  }
  @Override
  public boolean addOutStreamOpen(Packet packet, BoshSession bs) {
    packet.initVars(getFromAddress(bs.getSid().toString()), bs.getDataReceiver());

    return addOutPacketWithTimeout(packet, startedHandler, 15l, TimeUnit.SECONDS);
  }