public List<Presence> addMember(@Nonnull final JID targetUserAddress, @Nullable final String nickname, @Nonnull final Affiliation actorAffiliation) throws ForbiddenException, ConflictException
    {
        if (Affiliation.admin != actorAffiliation && Affiliation.owner != actorAffiliation) {
            throw new ForbiddenException();
        }

        final JID targetUserAddressBare = targetUserAddress.asBareJID();
        synchronized (this) {
            // Check if the desired nickname is already reserved for another member
            if (nickname != null && !nickname.trim().isEmpty() && members.containsValue(nickname.toLowerCase())) {
                if (!nickname.equals(members.get(targetUserAddressBare))) {
                    throw new ConflictException();
                }
            } else if (isLoginRestrictedToNickname() && (nickname == null || nickname.trim().isEmpty())) {
                throw new ConflictException();
            }
            // Check that the room always has an owner
            if (owners.contains(targetUserAddressBare) && owners.size() == 1) {
                throw new ConflictException();
            }
            // Check if user is already a member
            if (members.containsKey(targetUserAddressBare)) {
                // Do nothing
                return Collections.emptyList();
            }

            // Remove the user from other affiliation lists. This can throw a 'forbidden' when the actor tries to demote a higher-up!
            final Affiliation oldAffiliation = getAffiliation(targetUserAddressBare);
            switch (oldAffiliation) {
                case owner:
                    removeOwner(targetUserAddressBare, actorAffiliation);
                    break;
                case admin:
                    removeAdmin(targetUserAddressBare, actorAffiliation);
                    break;
                case outcast:
                    removeOutcast(targetUserAddressBare, actorAffiliation, null);  // No need to calculate actor role, as both the 'owner' and 'admin' affiliation is guaranteed to be allowed to perform this action.
                    break;
            }

            // Associate the reserved nickname with the bareJID. If nickname is null then associate an empty string
            members.put(targetUserAddressBare, (nickname == null ? "" : nickname.toLowerCase()));

            // Update the DB if the room is persistent
            MUCPersistenceManager.saveAffiliationToDB(
                this,
                targetUserAddressBare,
                nickname,
                Affiliation.member,
                oldAffiliation);
        }

        // apply the affiliation change, assigning a new affiliation
        // based on the group(s) of the affected user(s)
        return applyAffiliationChange(targetUserAddressBare, null, null, null);
    }
Adds a new user to the list of members.

@param targetUserAddress The (real) JID of the user to add as a member.
@param nickname The reserved nickname of the member for the room or null if none.
@param actorAffiliation the room affiliation of the user that is trying to modify the members list.
@return the list of updated presences of all the client resources that the client used to
        join the room.
@throws ForbiddenException If the user is not allowed to modify the members list.
@throws ConflictException If the desired room nickname is already reserved for the room or if
            the room was going to lose all its owners.
xmppserver/src/main/java/org/jivesoftware/openfire/muc/MUCRoom.java