@Override
    public IQ handleIQ(IQ packet) throws UnauthorizedException {
        LocalClientSession session = (LocalClientSession) sessionManager.getSession(packet.getFrom());
        // If no session was found then answer an error (if possible)
        if (session == null) {
            Log.warn("Error during resource binding. No session found for '{}'", packet.getFrom());
            // This error packet probably won't make it through
            IQ reply = IQ.createResultIQ(packet);
            reply.setChildElement(packet.getChildElement().createCopy());
            reply.setError(PacketError.Condition.internal_server_error);
            return reply;
        }

        IQ reply = IQ.createResultIQ(packet);
        reply.setFrom((String)null); // OF-2001: IQ bind requests are made from entities that have no resource yet. Responding with a 'from' value confuses many clients.
        Element child = reply.setChildElement("bind", "urn:ietf:params:xml:ns:xmpp-bind");
        // Check if the client specified a desired resource
        String resource = packet.getChildElement().elementTextTrim("resource");
        if (resource == null || resource.isEmpty()) {
            // None was defined so use the random generated resource
            resource = session.getAddress().getResource();
        }
        else {
            // Check that the desired resource is valid
            try {
                resource = JID.resourceprep(resource);
            }
            catch (IllegalArgumentException e) {
                reply.setChildElement(packet.getChildElement().createCopy());
                reply.setError(PacketError.Condition.jid_malformed);
                // Send the error directly since a route does not exist at this point.
                session.process(reply);
                return null;
            }
        }
        // Get the token that was generated during the SASL authentication
        AuthToken authToken = session.getAuthToken();
        if (authToken == null) {
            // User must be authenticated before binding a resource
            reply.setChildElement(packet.getChildElement().createCopy());
            reply.setError(PacketError.Condition.not_authorized);
            // Send the error directly since a route does not exist at this point.
            session.process(reply);
            return reply;
        }
        if (authToken.isAnonymous()) {
            // User used ANONYMOUS SASL so initialize the session as an anonymous login
            session.setAnonymousAuth();
        }
        else {
            String username = authToken.getUsername().toLowerCase();
            // If a session already exists with the requested JID, then check to see
            // if we should kick it off or refuse the new connection
            final JID desiredJid = new JID(username, serverName, resource, true);
            ClientSession oldSession = routingTable.getClientRoute(desiredJid);
            if (oldSession != null) {
                try {
                    if (oldSession.isClosed()) {
                        // If there's an old session that's already closed, then this could be a detached session. The
                        // new session does not conflict with the old one, but the old one needs to be cleaned up to
                        // prevent data consistency issues (OF-3044).
                        Log.debug("Instructing all cluster nodes to remove any detached session for '{}' as a new session is binding to that resource.", desiredJid);
                        CacheFactory.doSynchronousClusterTask(new ClientSessionTask(desiredJid, RemoteSessionTask.Operation.removeDetached), true);
                    }
                    else
                    {
                        Log.debug("Found a pre-existing, non-closed session for '{}'. Performing resource conflict resolution.", desiredJid);
                        int conflictLimit = sessionManager.getConflictKickLimit();
                        if (conflictLimit == SessionManager.NEVER_KICK) {
                            Log.debug("Conflict resolution configuration is 'NEVER KICK'. Rejecting the bind request with error condition 'conflict'.");
                            reply.setChildElement(packet.getChildElement().createCopy());
                            reply.setError(PacketError.Condition.conflict);
                            // Send the error directly since a route does not exist at this point.
                            session.process(reply);
                            return null;
                        }

                        int conflictCount = oldSession.incrementConflictCount();
                        if (conflictCount > conflictLimit) {
                            Log.debug("Kick out an old connection that is conflicting with a new one. Old session: {}", oldSession);
                            StreamError error = new StreamError(StreamError.Condition.conflict);
                            oldSession.deliverRawText(error.toXML());
                            oldSession.close(); // When living on a remote cluster node, this will prevent that session from becoming 'resumable'.

                            // OF-1923: As the session is now replaced, the old session will never be resumed.
                            if (oldSession instanceof LocalClientSession) {
                                // As the new session has already replaced the old session, we're not explicitly closing
                                // the old session again, as that would cause the state of the new session to be affected.
                                sessionManager.removeDetached((LocalClientSession) oldSession);
                            }
                        } else {
                            Log.debug("Conflict resolution configuration does not allow kicking of old session (yet). Conflict count: {}, conflict limit: {}. Rejecting the bind request with error condition 'conflict'.", conflictCount, conflictLimit);
                            reply.setChildElement(packet.getChildElement().createCopy());
                            reply.setError(PacketError.Condition.conflict);
                            // Send the error directly since a route does not exist at this point.
                            session.process(reply);
                            return null;
                        }
                    }
                }
                catch (Exception e) {
                    Log.error("Error during login", e);
                }
            }
            // If the connection was not refused due to conflict, log the user in
            session.setAuthToken(authToken, resource);
        }

        child.addElement("jid").setText(session.getAddress().toString());
        // Send the response directly since a route does not exist at this point.
        session.process(reply);
        // After the client has been informed, inform all listeners as well.
        SessionEventDispatcher.dispatchEvent(session, SessionEventDispatcher.EventType.resource_bound);
        return null;
    }
Binds a resource to the stream so that the client's address becomes a full JID. Once a resource
has been bound to the session the entity (i.e. client) is considered a "connected resource".
<p>
Clients may specify a desired resource but if none was specified then the server will create
a random resource for the session. The new resource should be in accordance with ResourcePrep.
The server will also verify if there are previous sessions from the same user that are already
using the resource specified by the user. Depending on the server configuration the old session
may be kicked or the new session may be rejected.</p>

@author Gaston Dombiak
xmppserver/src/main/java/org/jivesoftware/openfire/handler/IQBindHandler.java