private void modifyNodeAffiliations(PubSubService service, IQ iq, Element entitiesElement) {
        String nodeID = entitiesElement.attributeValue("node");
        if (nodeID == null) {
            // NodeID was not provided. Return bad-request error.
            sendErrorPacket(iq, PacketError.Condition.bad_request, null);
            return;
        }
        Node node = service.getNode(nodeID);
        if (node == null) {
            // Node does not exist. Return item-not-found error.
            sendErrorPacket(iq, PacketError.Condition.item_not_found, null);
            return;
        }
        if (!node.isAdmin(iq.getFrom())) {
            // Requesting entity is prohibited from getting affiliates list. Return forbidden error.
            sendErrorPacket(iq, PacketError.Condition.forbidden, null);
            return;
        }

        IQ reply = IQ.createResultIQ(iq);
        Collection<JID> invalidAffiliates = new ArrayList<>();

        // Process modifications or creations of affiliations
        for (Iterator<Element> it = entitiesElement.elementIterator("affiliation"); it.hasNext();) {
            Element affiliation = it.next();
            JID owner = new JID(affiliation.attributeValue("jid"));
            String newAffiliation = affiliation.attributeValue("affiliation");
            // Get current affiliation of this user (if any)
            NodeAffiliate affiliate = node.getAffiliate(owner);

            // Check that we are not removing the only owner of the node
            if (affiliate != null && !affiliate.getAffiliation().name().equals(newAffiliation)) {
                // Trying to modify an existing affiliation
                if (affiliate.getAffiliation() == NodeAffiliate.Affiliation.owner &&
                        node.getOwners().size() == 1) {
                    // Trying to remove the unique owner of the node. Include in error answer.
                    invalidAffiliates.add(owner);
                    continue;
                }
            }

            // Owner is setting affiliations for new entities or modifying
            // existing affiliations
            if ("owner".equals(newAffiliation)) {
                node.addOwner(owner);
            }
            else if ("publisher".equals(newAffiliation)) {
                node.addPublisher(owner);
            }
            else if ("none".equals(newAffiliation)) {
                node.addNoneAffiliation(owner);
            }
            else  {
                node.addOutcast(owner);
            }
        }

        // Process invalid entities that tried to remove node owners. Send original affiliation
        // of the invalid entities.
        if (!invalidAffiliates.isEmpty()) {
            reply.setError(PacketError.Condition.not_acceptable);
            Element child =
                    reply.setChildElement("pubsub", "http://jabber.org/protocol/pubsub#owner");
            Element entities = child.addElement("affiliations");
            if (!node.isRootCollectionNode()) {
                entities.addAttribute("node", node.getUniqueIdentifier().getNodeId());
            }
            for (JID affiliateJID : invalidAffiliates) {
                NodeAffiliate affiliate = node.getAffiliate(affiliateJID);
                Element entity = entities.addElement("affiliation");
                entity.addAttribute("jid", affiliate.getJID().toString());
                entity.addAttribute("affiliation", affiliate.getAffiliation().name());
            }
        }
        // Send reply
        router.route(reply);
    }
Checks if the following conditions are satisfied and creates a node
- Requester can create nodes
- Instant node creation is enabled
- Node does not already exist
- New node configuration is valid

<br>NOTE 1: This method should not reply to the client
<br>NOTE 2: This method calls UserManager::isRegisteredUser(JID) which can block waiting for a response - so
do not call this method in the same thread in which a response might arrive

@param service The service instance that's responsible for processing (cannot be null)
@param requester The (full) JID of the entity that performs the action (cannot be null)
@param configuration Optional Configuration dataform, if user requested to configure the node (can be null)
@param nodeID The ID of the node to be created, or null when an instant node is to be created.
@param publishOptions Optional Publishing Options, which are either preconditions or configuration overrides (can be null)
@return {@link CreateNodeResponse}
xmppserver/src/main/java/org/jivesoftware/openfire/pubsub/PubSubEngine.java