public static CreateNodeResponse createNodeHelper(PubSubService service, JID requester, Element configuration, String nodeID, DataForm publishOptions) {
        // Verify that sender has permissions to create nodes
        // TODO OF-2506: figure out why anonymous users should not be allowed to create a node. There is no way to check if remote users are anonymous anyway.
        if (!service.canCreateNode(requester) || (!isComponent(requester) && !UserManager.getInstance().isRegisteredUser(requester, true))) {
            // The user is not allowed to create nodes so return an error
            return new CreateNodeResponse(PacketError.Condition.forbidden, null, null);
        }
        DataForm completedForm = null;
        CollectionNode parentNode = null;
        String newNodeID = nodeID;
        if (nodeID == null) {
            // User requested an instant node
            if (!service.isInstantNodeSupported()) {
                // Instant nodes creation is not allowed so return an error
                Element pubsubError = DocumentHelper.createElement(
                        QName.get("nodeid-required", "http://jabber.org/protocol/pubsub#errors"));
                return new CreateNodeResponse(PacketError.Condition.not_acceptable, pubsubError, null);
            }
            do {
                // Create a new nodeID and make sure that the random generated string does not
                // match an existing node. Probability to match an existing node are very very low
                // but they exist :)
                newNodeID = StringUtils.randomString(15);
            }
            while (service.getNode(newNodeID) != null);
        }
        boolean collectionType = false;

        // Check if user requested to configure the node (using a data form)
        if (configuration!= null) {
            // Get the data form that contains the parent nodeID
            completedForm = getSentConfigurationForm( configuration );
        }

        if (publishOptions != null) {
            // Apply publish options to override provided config.
            if ( completedForm == null ) {
                completedForm = publishOptions;
            } else {
                for ( final FormField publishOption : publishOptions.getFields() ) {
                    completedForm.removeField( publishOption.getVariable() );
                    final FormField formField = completedForm.addField( publishOption.getVariable(), publishOption.getLabel(), publishOption.getType() );
                    for ( final String value : publishOption.getValues() ) {
                        formField.addValue( value );
                    }
                }
            }
        }

        if (completedForm != null) {
            // Calculate newNodeID when new node is affiliated with a Collection
            FormField field = completedForm.getField("pubsub#collection");
            if (field != null) {
                List<String> values = field.getValues();
                if (!values.isEmpty()) {
                    String parentNodeID = values.get(0);
                    Node tempNode = service.getNode(parentNodeID);
                    if (tempNode == null) {
                        // Requested parent node was not found so return an error
                        return new CreateNodeResponse(PacketError.Condition.item_not_found, null, null);
                    }
                    else if (!tempNode.isCollectionNode()) {
                        // Requested parent node is not a collection node so return an error
                        return new CreateNodeResponse(PacketError.Condition.not_acceptable, null, null);
                    }
                    parentNode = (CollectionNode) tempNode;
                }
            }
            field = completedForm.getField("pubsub#node_type");
            if (field != null) {
                // Check if user requested to create a new collection node
                List<String> values = field.getValues();
                if (!values.isEmpty()) {
                    collectionType = "collection".equals(values.get(0));
                }
            }
        }

        // If no parent was defined then use the root collection node
        if (parentNode == null && service.isCollectionNodesSupported()) {
            parentNode = service.getRootCollectionNode();
        }
        // Check that the requested nodeID does not exist
        Node existingNode = service.getNode(newNodeID);
        if (existingNode != null) {
            // There is a conflict since a node with the same ID already exists
            return new CreateNodeResponse(PacketError.Condition.conflict, null, null);
        }

        if (collectionType && !service.isCollectionNodesSupported()) {
            // Cannot create a collection node since the service doesn't support it
            Element pubsubError = DocumentHelper.createElement(
                    QName.get("unsupported", "http://jabber.org/protocol/pubsub#errors"));
            pubsubError.addAttribute("feature", "collections");
            return new CreateNodeResponse(PacketError.Condition.feature_not_implemented, pubsubError, null);
        }

        if (parentNode != null && !collectionType) {
            // Check if requester is allowed to add a new leaf child node to the parent node
            if (!parentNode.isAssociationAllowed(requester)) {
                // User is not allowed to add child leaf node to parent node. Return an error.
                return new CreateNodeResponse(PacketError.Condition.forbidden, null, null);
            }
            // Check if number of child leaf nodes has not been exceeded
            if (parentNode.isMaxLeafNodeReached()) {
                // Max number of child leaf nodes has been reached. Return an error.
                Element pubsubError = DocumentHelper.createElement(QName.get("max-nodes-exceeded",
                        "http://jabber.org/protocol/pubsub#errors"));
                return new CreateNodeResponse(PacketError.Condition.conflict, pubsubError, null);
            }
        }

        // Create and configure the node
        boolean conflict = false;
        Node newNode = null;
        try {
            // TODO Assumed that the owner of the subscription is the bare JID of the subscription JID. Waiting StPeter answer for explicit field.
            JID owner = requester.asBareJID();
            final DefaultNodeConfiguration defaultConfiguration = service.getDefaultNodeConfiguration( !collectionType );
            synchronized ( (newNodeID + MUTEX_SUFFIX_NODE).intern()) {
                if (service.getNode(newNodeID) == null) {
                    // Create the node
                    if (collectionType) {
                        newNode = new CollectionNode(service.getUniqueIdentifier(), parentNode, newNodeID, requester, defaultConfiguration);
                    }
                    else {
                        newNode = new LeafNode(service.getUniqueIdentifier(), parentNode, newNodeID, requester, defaultConfiguration);
                    }
                    // Add the creator as the node owner
                    newNode.addOwner(owner);
                    // Configure and save the node to the backend store
                    if (completedForm != null) {
                        newNode.configure(completedForm);
                    }
                    else {
                        newNode.saveToDB();
                    }

                    // TODO Replace with a cluster task that does not interact with the database (OF-2141).
                    CacheFactory.doClusterTask(new RefreshNodeTask(newNode));
                }
                else {
                    conflict = true;
                }
            }
            if (conflict) {
                // There is a conflict since a node with the same ID already exists
                return new CreateNodeResponse(PacketError.Condition.conflict, null, null);
            }
            else {
                // Return success to the node owner
                return new CreateNodeResponse(null, null, newNode);
            }
        }
        catch (NotAcceptableException e) {
            // Node should have at least one owner. Return not-acceptable error.
            return new CreateNodeResponse(PacketError.Condition.not_acceptable, null, null);
        }
    }
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