Bläddra i källkod

Server: First create all child nodes and references, then call the constructors

Julius Pfrommer 6 år sedan
förälder
incheckning
3c96b6fc33
2 ändrade filer med 292 tillägg och 182 borttagningar
  1. 10 2
      src/server/ua_server_ns0.c
  2. 282 180
      src/server/ua_services_nodemanagement.c

+ 10 - 2
src/server/ua_server_ns0.c

@@ -96,6 +96,14 @@ UA_Server_createNS0_base(UA_Server *server) {
     ret |= addNode_raw(server, UA_NODECLASS_REFERENCETYPE, UA_NS0ID_HASSUBTYPE, "HasSubtype",
                        &hassubtype_attr, &UA_TYPES[UA_TYPES_REFERENCETYPEATTRIBUTES]);
 
+    UA_ReferenceTypeAttributes aggregates_attr = UA_ReferenceTypeAttributes_default;
+    aggregates_attr.displayName = UA_LOCALIZEDTEXT("", "Aggregates");
+    aggregates_attr.isAbstract = false;
+    aggregates_attr.symmetric = false;
+    aggregates_attr.inverseName = UA_LOCALIZEDTEXT("", "AggregatedBy");
+    ret |= addNode_raw(server, UA_NODECLASS_REFERENCETYPE, UA_NS0ID_AGGREGATES, "Aggregates",
+                       &aggregates_attr, &UA_TYPES[UA_TYPES_REFERENCETYPEATTRIBUTES]);
+
     ret |= addReferenceTypeNode(server, "HierarchicalReferences", NULL,
                          UA_NS0ID_HIERARCHICALREFERENCES, true, false, UA_NS0ID_REFERENCES);
 
@@ -126,8 +134,8 @@ UA_Server_createNS0_base(UA_Server *server) {
     ret |= addReferenceTypeNode(server, "GeneratesEvent", "GeneratedBy", UA_NS0ID_GENERATESEVENT,
                          false, false, UA_NS0ID_NONHIERARCHICALREFERENCES);
 
-    ret |= addReferenceTypeNode(server, "Aggregates", "AggregatedBy", UA_NS0ID_AGGREGATES,
-                         false, false, UA_NS0ID_HASCHILD);
+    /* Complete bootstrap of Aggregates */
+    ret |= addNode_finish(server, UA_NS0ID_AGGREGATES, UA_NS0ID_HASCHILD, UA_NS0ID_HASSUBTYPE);
 
     /* Complete bootstrap of HasSubtype */
     ret |= addNode_finish(server, UA_NS0ID_HASSUBTYPE, UA_NS0ID_HASCHILD, UA_NS0ID_HASSUBTYPE);

+ 282 - 180
src/server/ua_services_nodemanagement.c

@@ -36,12 +36,26 @@ UA_Server_getNodeContext(UA_Server *server, UA_NodeId nodeId,
     const UA_Node *node = UA_Nodestore_get(server, &nodeId);
     if(!node)
         return UA_STATUSCODE_BADNODEIDUNKNOWN;
-
     *nodeContext = node->context;
     UA_Nodestore_release(server, node);
     return UA_STATUSCODE_GOOD;
 }
 
+static UA_StatusCode
+setDeconstructedNode(UA_Server *server, UA_Session *session,
+                     UA_Node *node, void *context) {
+    node->constructed = false;
+    return UA_STATUSCODE_GOOD;
+}
+
+static UA_StatusCode
+setConstructedNodeContext(UA_Server *server, UA_Session *session,
+                          UA_Node *node, void *context) {
+    node->context = context;
+    node->constructed = true;
+    return UA_STATUSCODE_GOOD;
+}
+
 static UA_StatusCode
 editNodeContext(UA_Server *server, UA_Session* session,
                 UA_Node* node, void *context) {
@@ -328,7 +342,7 @@ findChildByBrowsename(UA_Server *server, UA_Session *session,
     bd.includeSubtypes = true;
     bd.browseDirection = UA_BROWSEDIRECTION_FORWARD;
     bd.nodeClassMask = UA_NODECLASS_OBJECT | UA_NODECLASS_VARIABLE | UA_NODECLASS_METHOD;
-    bd.resultMask = UA_BROWSERESULTMASK_NODECLASS | UA_BROWSERESULTMASK_BROWSENAME;
+    bd.resultMask = UA_BROWSERESULTMASK_BROWSENAME;
 
     UA_BrowseResult br;
     UA_BrowseResult_init(&br);
@@ -384,30 +398,32 @@ isMandatoryChild(UA_Server *server, UA_Session *session,
 }
 
 static UA_StatusCode
-copyChildNodes(UA_Server *server, UA_Session *session,
-               const UA_NodeId *sourceNodeId,
-               const UA_NodeId *destinationNodeId);
+copyAllChildren(UA_Server *server, UA_Session *session,
+                const UA_NodeId *source, const UA_NodeId *destination);
+
+static UA_StatusCode
+recursiveTypeCheckAddChildren(UA_Server *server, UA_Session *session,
+                              const UA_Node *node, const UA_Node *type);
 
 static void
 Operation_addReference(UA_Server *server, UA_Session *session, void *context,
                        const UA_AddReferencesItem *item, UA_StatusCode *retval);
 
 static UA_StatusCode
-copyChildNode(UA_Server *server, UA_Session *session,
-              const UA_NodeId *destinationNodeId,
-              const UA_ReferenceDescription *rd) {
+copyChild(UA_Server *server, UA_Session *session, const UA_NodeId *destinationNodeId,
+          const UA_ReferenceDescription *rd) {
+    /* Is there an existing child with the browsename? */
     UA_NodeId existingChild = UA_NODEID_NULL;
-    UA_StatusCode retval =
-        findChildByBrowsename(server, session, destinationNodeId,
-                              &rd->browseName, &existingChild);
+    UA_StatusCode retval = findChildByBrowsename(server, session, destinationNodeId,
+                                                 &rd->browseName, &existingChild);
     if(retval != UA_STATUSCODE_GOOD)
         return retval;
 
-    /* Have a child with that browseName. Try to deep-copy missing members. */
+    /* Have a child with that browseName. Deep-copy missing members. */
     if(!UA_NodeId_isNull(&existingChild)) {
         if(rd->nodeClass == UA_NODECLASS_VARIABLE ||
            rd->nodeClass == UA_NODECLASS_OBJECT)
-            retval = copyChildNodes(server, session, &rd->nodeId.nodeId, &existingChild);
+            retval = copyAllChildren(server, session, &rd->nodeId.nodeId, &existingChild);
         UA_NodeId_deleteMembers(&existingChild);
         return retval;
     }
@@ -416,9 +432,8 @@ copyChildNode(UA_Server *server, UA_Session *session,
     if(!isMandatoryChild(server, session, &rd->nodeId.nodeId))
         return UA_STATUSCODE_GOOD;
 
-    /* No existing child with that browsename. Create it. */
+    /* Child is a method -> create a reference */
     if(rd->nodeClass == UA_NODECLASS_METHOD) {
-        /* Add a reference to the method in the objecttype */
         UA_AddReferencesItem newItem;
         UA_AddReferencesItem_init(&newItem);
         newItem.sourceNodeId = *destinationNodeId;
@@ -430,23 +445,18 @@ copyChildNode(UA_Server *server, UA_Session *session,
         return retval;
     }
 
-    /* Node exists and is a variable or object. Instantiate missing mandatory
-     * children */
+    /* Child is a variable or object */
     if(rd->nodeClass == UA_NODECLASS_VARIABLE ||
        rd->nodeClass == UA_NODECLASS_OBJECT) {
-        /* Get the node */
+        /* Make a copy of the node */
         UA_Node *node;
         retval = UA_Nodestore_getCopy(server, &rd->nodeId.nodeId, &node);
         if(retval != UA_STATUSCODE_GOOD)
             return retval;
 
-        /* Get the type */
-        const UA_Node *type = getNodeType(server, node);
-        const UA_NodeId *typeId;
-        if(type)
-            typeId = &type->nodeId;
-        else
-            typeId = &UA_NODEID_NULL;
+        /* Remove the context of the copied node */
+        node->context = NULL;
+        node->constructed = false;
 
         /* Reset the NodeId (random numeric id will be assigned in the nodestore) */
         UA_NodeId_deleteMembers(&node->nodeId);
@@ -463,52 +473,40 @@ copyChildNode(UA_Server *server, UA_Session *session,
         /* Add the node to the nodestore */
         UA_NodeId newNodeId;
         retval = UA_Nodestore_insert(server, node, &newNodeId);
-        if(retval != UA_STATUSCODE_GOOD) {
-            UA_Nodestore_release(server, type);
+        if(retval != UA_STATUSCODE_GOOD)
             return retval;
-        }
-
-        /* Add all the children of this child to the new child node to make sure we take
-         * the values from the nearest inherited object first.
-         * The call to addNode_finish will then only add the children from the type and
-         * thus skip the direct children of rd->nodeId.nodeId */
-        copyChildNodes(server, session, &rd->nodeId.nodeId, &newNodeId);
 
-        /* Add the parent reference */
+        /* Add the node references */
         retval = AddNode_addRefs(server, session, &newNodeId, destinationNodeId,
-                                 &rd->referenceTypeId, typeId);
+                                 &rd->referenceTypeId, &rd->typeDefinition.nodeId);
         if(retval != UA_STATUSCODE_GOOD) {
-            UA_Nodestore_remove(server, &node->nodeId);
-            UA_Nodestore_release(server, type);
+            UA_Nodestore_remove(server, &newNodeId);
             return retval;
         }
 
-        /* Call addnode_finish, this recursively adds additional members, the type
-         * definition and so on of the base type of this child, if they are not yet
-         * in the destination */
-        retval = AddNode_finish(server, session, &newNodeId);
-        UA_NodeId_deleteMembers(&newNodeId);
-        UA_Nodestore_release(server, type);
-        if(retval != UA_STATUSCODE_GOOD)
-            return retval;
+        /* For the new child, recursively copy the members of the original. No
+         * typechecking is performed here. Assuming that the original is
+         * consistent. */
+        retval = copyAllChildren(server, session, &rd->nodeId.nodeId, &newNodeId);
     }
+
     return retval;
 }
 
 /* Copy any children of Node sourceNodeId to another node destinationNodeId. */
 static UA_StatusCode
-copyChildNodes(UA_Server *server, UA_Session *session,
-               const UA_NodeId *sourceNodeId, const UA_NodeId *destinationNodeId) {
+copyAllChildren(UA_Server *server, UA_Session *session,
+                const UA_NodeId *source, const UA_NodeId *destination) {
     /* Browse to get all children of the source */
     UA_BrowseDescription bd;
     UA_BrowseDescription_init(&bd);
-    bd.nodeId = *sourceNodeId;
+    bd.nodeId = *source;
     bd.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_AGGREGATES);
     bd.includeSubtypes = true;
     bd.browseDirection = UA_BROWSEDIRECTION_FORWARD;
     bd.nodeClassMask = UA_NODECLASS_OBJECT | UA_NODECLASS_VARIABLE | UA_NODECLASS_METHOD;
     bd.resultMask = UA_BROWSERESULTMASK_REFERENCETYPEID | UA_BROWSERESULTMASK_NODECLASS |
-        UA_BROWSERESULTMASK_BROWSENAME;
+        UA_BROWSERESULTMASK_BROWSENAME | UA_BROWSERESULTMASK_TYPEDEFINITION;
 
     UA_BrowseResult br;
     UA_BrowseResult_init(&br);
@@ -520,7 +518,7 @@ copyChildNodes(UA_Server *server, UA_Session *session,
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
     for(size_t i = 0; i < br.referencesSize; ++i) {
         UA_ReferenceDescription *rd = &br.references[i];
-        retval = copyChildNode(server, session, destinationNodeId, rd);
+        retval = copyChild(server, session, destination, rd);
         if(retval != UA_STATUSCODE_GOOD)
             return retval;
     }
@@ -530,8 +528,8 @@ copyChildNodes(UA_Server *server, UA_Session *session,
 }
 
 static UA_StatusCode
-addChildren(UA_Server *server, UA_Session *session,
-            const UA_Node *node, const UA_Node *type) {
+addTypeChildren(UA_Server *server, UA_Session *session,
+                const UA_Node *node, const UA_Node *type) {
     /* Get the hierarchy of the type and all its supertypes */
     UA_NodeId *hierarchy = NULL;
     size_t hierarchySize = 0;
@@ -542,7 +540,7 @@ addChildren(UA_Server *server, UA_Session *session,
 
     /* Copy members of the type and supertypes (and instantiate them) */
     for(size_t i = 0; i < hierarchySize; ++i) {
-        retval = copyChildNodes(server, session, &hierarchy[i], &node->nodeId);
+        retval = copyAllChildren(server, session, &hierarchy[i], &node->nodeId);
         if(retval != UA_STATUSCODE_GOOD)
             goto cleanup;
     }
@@ -552,49 +550,6 @@ cleanup:
     return retval;
 }
 
-/* Calls the global destructor internally of the global constructor succeeds and
- * the type-level constructor fails. */
-static UA_StatusCode callConstructors(UA_Server *server, UA_Session *session,
-                                      const UA_Node *node, const UA_Node *type) {
-    /* Get the node type constructor */
-    const UA_NodeTypeLifecycle *lifecycle = NULL;
-    if(node->nodeClass == UA_NODECLASS_OBJECT) {
-        const UA_ObjectTypeNode *ot = (const UA_ObjectTypeNode*)type;
-        lifecycle = &ot->lifecycle;
-    } else if(node->nodeClass == UA_NODECLASS_VARIABLE) {
-        const UA_VariableTypeNode *vt = (const UA_VariableTypeNode*)type;
-        lifecycle = &vt->lifecycle;
-    }
-
-    /* Call the global constructor */
-    void *context = node->context;
-    UA_StatusCode retval = UA_STATUSCODE_GOOD;
-    if(server->config.nodeLifecycle.constructor)
-        retval = server->config.nodeLifecycle.constructor(server, &session->sessionId,
-                                                          session->sessionHandle,
-                                                          &node->nodeId, &context);
-
-    /* Call the type constructor */
-    if(retval == UA_STATUSCODE_GOOD && lifecycle && lifecycle->constructor)
-        retval = lifecycle->constructor(server, &session->sessionId,
-                                        session->sessionHandle, &type->nodeId,
-                                        type->context, &node->nodeId, &context);
-
-    /* Set the context *and* mark the node as constructed */
-    if(retval == UA_STATUSCODE_GOOD)
-        retval = UA_Server_editNode(server, &server->adminSession, &node->nodeId,
-                                    (UA_EditNodeCallback)editNodeContext,
-                                    context);
-
-    /* Fail. Call the global destructor. */
-    if(retval != UA_STATUSCODE_GOOD && server->config.nodeLifecycle.destructor)
-        server->config.nodeLifecycle.destructor(server, &session->sessionId,
-                                                session->sessionHandle,
-                                                &node->nodeId, context);
-
-    return retval;
-}
-
 static UA_StatusCode
 addRef(UA_Server *server, UA_Session *session, const UA_NodeId *nodeId,
        const UA_NodeId *referenceTypeId, const UA_NodeId *parentNodeId,
@@ -614,10 +569,6 @@ addRef(UA_Server *server, UA_Session *session, const UA_NodeId *nodeId,
 /* Add Node */
 /************/
 
-static void
-removeDeconstructedNode(UA_Server *server, UA_Session *session,
-                        const UA_Node *node, UA_Boolean removeTargetRefs);
-
 static const UA_NodeId hasSubtype = {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_HASSUBTYPE}};
 
 UA_StatusCode
@@ -911,6 +862,170 @@ Operation_addNode_begin(UA_Server *server, UA_Session *session, void *nodeContex
     return retval;
 }
 
+static UA_StatusCode
+recursiveTypeCheckAddChildren(UA_Server *server, UA_Session *session,
+                              const UA_Node *node, const UA_Node *type) {
+    UA_assert(node != NULL);
+    UA_assert(type != NULL);
+    UA_StatusCode retval = UA_STATUSCODE_GOOD;
+
+    /* Use attributes from the type. The value and value constraints are the
+     * same for the variable and variabletype attribute structs. */
+    if(node->nodeClass == UA_NODECLASS_VARIABLE ||
+       node->nodeClass == UA_NODECLASS_VARIABLETYPE) {
+        retval = useVariableTypeAttributes(server, session, (const UA_VariableNode**)&node,
+                                           (const UA_VariableTypeNode*)type);
+        if(retval != UA_STATUSCODE_GOOD) {
+            UA_LOG_NODEID_WRAP(&node->nodeId, UA_LOG_INFO_SESSION(&server->config.logger, session,
+                               "AddNodes: Using attributes for %.*s from the variable type "
+                               "failed with error code %s", (int)nodeIdStr.length,
+                               nodeIdStr.data, UA_StatusCode_name(retval)));
+            return retval;
+        }
+
+        /* Check if all attributes hold the constraints of the type now. The initial
+         * attributes must type-check. The constructor might change the attributes
+         * again. Then, the changes are type-checked by the normal write service. */
+        retval = typeCheckVariableNode(server, session, (const UA_VariableNode*)node,
+                                       (const UA_VariableTypeNode*)type);
+        if(retval != UA_STATUSCODE_GOOD) {
+            UA_LOG_NODEID_WRAP(&node->nodeId, UA_LOG_INFO_SESSION(&server->config.logger, session,
+                               "AddNodes: Type-checking the variable node %.*s "
+                               "failed with error code %s", (int)nodeIdStr.length,
+                               nodeIdStr.data, UA_StatusCode_name(retval)));
+            return retval;
+        }
+    }
+
+    /* Add (mandatory) child nodes from the type definition */
+    if(node->nodeClass == UA_NODECLASS_VARIABLE ||
+       node->nodeClass == UA_NODECLASS_OBJECT) {
+        retval = addTypeChildren(server, session, node, type);
+        if(retval != UA_STATUSCODE_GOOD) {
+            UA_LOG_NODEID_WRAP(&node->nodeId, UA_LOG_INFO_SESSION(&server->config.logger, session,
+                               "AddNodes: Adding child nodes of  %.*s failed with error code %s",
+                               (int)nodeIdStr.length, nodeIdStr.data, UA_StatusCode_name(retval)));
+        }
+    }
+
+    return UA_STATUSCODE_GOOD;
+}
+
+/* Construct children first */
+static UA_StatusCode
+recursiveCallConstructors(UA_Server *server, UA_Session *session,
+                          const UA_Node *node, const UA_Node *type) {
+    if(node->constructed)
+        return UA_STATUSCODE_GOOD;
+    
+    /* Construct the children */
+    UA_BrowseDescription bd;
+    UA_BrowseDescription_init(&bd);
+    bd.nodeId = node->nodeId;
+    bd.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_AGGREGATES);
+    bd.includeSubtypes = true;
+    bd.browseDirection = UA_BROWSEDIRECTION_FORWARD;
+
+    UA_BrowseResult br;
+    UA_BrowseResult_init(&br);
+    UA_UInt32 maxrefs = 0;
+    Operation_Browse(server, session, &maxrefs, &bd, &br);
+    if(br.statusCode != UA_STATUSCODE_GOOD)
+        return br.statusCode;
+
+    /* Call the constructor for every unconstructed node */
+    UA_StatusCode retval = UA_STATUSCODE_GOOD;
+    for(size_t i = 0; i < br.referencesSize; ++i) {
+        UA_ReferenceDescription *rd = &br.references[i];
+        const UA_Node *target = UA_Nodestore_get(server, &rd->nodeId.nodeId);
+        if(!target)
+            continue;
+        if(target->constructed) {
+            UA_Nodestore_release(server, target);
+            continue;
+        }
+
+        const UA_Node *targetType = NULL;
+        if(node->nodeClass == UA_NODECLASS_VARIABLE ||
+           node->nodeClass == UA_NODECLASS_OBJECT) {
+            targetType = getNodeType(server, target);
+            if(!targetType) {
+                UA_Nodestore_release(server, target);
+                retval = UA_STATUSCODE_BADTYPEDEFINITIONINVALID;
+                break;
+            }
+        }
+        retval = recursiveCallConstructors(server, session, target, targetType);
+        UA_Nodestore_release(server, target);
+        if(targetType)
+            UA_Nodestore_release(server, targetType);
+        if(retval != UA_STATUSCODE_GOOD)
+            break;
+    }
+
+    UA_BrowseResult_deleteMembers(&br);
+
+    /* If a child could not be constructed or the node is already constructed */
+    if(retval != UA_STATUSCODE_GOOD)
+        return retval;
+
+    /* Get the node type constructor */
+    const UA_NodeTypeLifecycle *lifecycle = NULL;
+    if(type && node->nodeClass == UA_NODECLASS_OBJECT) {
+        const UA_ObjectTypeNode *ot = (const UA_ObjectTypeNode*)type;
+        lifecycle = &ot->lifecycle;
+    } else if(type && node->nodeClass == UA_NODECLASS_VARIABLE) {
+        const UA_VariableTypeNode *vt = (const UA_VariableTypeNode*)type;
+        lifecycle = &vt->lifecycle;
+    }
+
+    /* Call the global constructor */
+    void *context = node->context;
+    if(server->config.nodeLifecycle.constructor)
+        retval = server->config.nodeLifecycle.constructor(server, &session->sessionId,
+                                                          session->sessionHandle,
+                                                          &node->nodeId, &context);
+
+    /* Call the type constructor */
+    if(retval == UA_STATUSCODE_GOOD && lifecycle && lifecycle->constructor)
+        retval = lifecycle->constructor(server, &session->sessionId,
+                                        session->sessionHandle, &type->nodeId,
+                                        type->context, &node->nodeId, &context);
+    if(retval != UA_STATUSCODE_GOOD)
+        goto fail1;
+
+    /* Set the context *and* mark the node as constructed */
+    if(retval == UA_STATUSCODE_GOOD)
+        retval = UA_Server_editNode(server, &server->adminSession, &node->nodeId,
+                                    (UA_EditNodeCallback)setConstructedNodeContext,
+                                    context);
+
+    /* All good, return */
+    if(retval == UA_STATUSCODE_GOOD)
+        return retval;
+
+    /* Fail. Call the destructors. */
+    if(lifecycle && lifecycle->destructor)
+        lifecycle->destructor(server, &session->sessionId,
+                              session->sessionHandle, &type->nodeId,
+                              type->context, &node->nodeId, &context);
+
+ fail1:
+    if(server->config.nodeLifecycle.destructor)
+        server->config.nodeLifecycle.destructor(server, &session->sessionId,
+                                                session->sessionHandle,
+                                                &node->nodeId, context);
+    return retval;
+}
+
+static void
+recursiveDeconstructNode(UA_Server *server, UA_Session *session,
+                         const UA_Node *node);
+
+static void
+recursiveDeleteNode(UA_Server *server, UA_Session *session,
+                    const UA_Node *node, UA_Boolean removeTargetRefs);
+
 /* Children, references, type-checking, constructors. */
 UA_StatusCode
 AddNode_finish(UA_Server *server, UA_Session *session, const UA_NodeId *nodeId) {
@@ -933,69 +1048,34 @@ AddNode_finish(UA_Server *server, UA_Session *session, const UA_NodeId *nodeId)
             if(server->bootstrapNS0)
                 goto constructor;
             UA_LOG_NODEID_WRAP(&node->nodeId, UA_LOG_INFO_SESSION(&server->config.logger, session,
-                                   "AddNodes: Node type for %.*s not found",
-                                   (int)nodeIdStr.length, nodeIdStr.data));
+                               "AddNodes: Node type for %.*s not found",
+                               (int)nodeIdStr.length, nodeIdStr.data));
             retval = UA_STATUSCODE_BADTYPEDEFINITIONINVALID;
             goto cleanup;
         }
 
-        /* Use attributes from the type. The value and value constraints are the
-         * same for the variable and variabletype attribute structs. */
-        if(node->nodeClass == UA_NODECLASS_VARIABLE ||
-           node->nodeClass == UA_NODECLASS_VARIABLETYPE) {
-            retval = useVariableTypeAttributes(server, session,
-                                               (const UA_VariableNode**)&node,
-                                               (const UA_VariableTypeNode*)type);
-            if(retval != UA_STATUSCODE_GOOD) {
-                UA_LOG_NODEID_WRAP(&node->nodeId, UA_LOG_INFO_SESSION(&server->config.logger, session,
-                                    "AddNodes: Using attributes for %.*s from the variable type "
-                                    "failed with error code %s",
-                                    (int)nodeIdStr.length, nodeIdStr.data, UA_StatusCode_name(retval)));
-                goto cleanup;
-            }
-
-            /* Check if all attributes hold the constraints of the type now. The initial
-             * attributes must type-check. The constructor might change the attributes
-             * again. Then, the changes are type-checked by the normal write service. */
-            retval = typeCheckVariableNode(server, session, (const UA_VariableNode*)node,
-                                           (const UA_VariableTypeNode*)type);
-            if(retval != UA_STATUSCODE_GOOD) {
-                UA_LOG_NODEID_WRAP(&node->nodeId, UA_LOG_INFO_SESSION(&server->config.logger, session,
-                                    "AddNodes: Type-checking the variable node %.*s "
-                                    "failed with error code %s",
-                                    (int)nodeIdStr.length, nodeIdStr.data, UA_StatusCode_name(retval)));
-                goto cleanup;
-            }
-        }
-
-        /* Add (mandatory) child nodes from the type definition */
-        if(node->nodeClass == UA_NODECLASS_VARIABLE ||
-           node->nodeClass == UA_NODECLASS_OBJECT) {
-            retval = addChildren(server, session, node, type);
-            if(retval != UA_STATUSCODE_GOOD) {
-                UA_LOG_NODEID_WRAP(&node->nodeId, UA_LOG_INFO_SESSION(&server->config.logger, session,
-                                    "AddNodes: Adding child nodes of  %.*s failed with error code %s",
-                                    (int)nodeIdStr.length, nodeIdStr.data, UA_StatusCode_name(retval)));
-                goto cleanup;
-            }
-        }
+        retval = recursiveTypeCheckAddChildren(server, session, node, type);
+        if(retval != UA_STATUSCODE_GOOD)
+            goto cleanup;
     }
 
     /* Call the constructor(s) */
  constructor:
-    retval = callConstructors(server, session, node, type);
+    retval = recursiveCallConstructors(server, session, node, type);
     if(retval != UA_STATUSCODE_GOOD) {
         UA_LOG_NODEID_WRAP(&node->nodeId, UA_LOG_INFO_SESSION(&server->config.logger, session,
-                            "AddNodes: Calling the node constructor(s) of %.*s failed "
-                            "with status code %s",
-                            (int)nodeIdStr.length, nodeIdStr.data, UA_StatusCode_name(retval)));
+                           "AddNodes: Calling the node constructor(s) of %.*s failed "
+                           "with status code %s", (int)nodeIdStr.length,
+                           nodeIdStr.data, UA_StatusCode_name(retval)));
     }
 
  cleanup:
     if(type)
         UA_Nodestore_release(server, type);
-    if(retval != UA_STATUSCODE_GOOD)
-        removeDeconstructedNode(server, session, node, true);
+    if(retval != UA_STATUSCODE_GOOD) {
+        recursiveDeconstructNode(server, session, node);
+        recursiveDeleteNode(server, session, node, true);
+    }
     UA_Nodestore_release(server, node);
     return retval;
 }
@@ -1125,9 +1205,15 @@ removeIncomingReferences(UA_Server *server, UA_Session *session,
     }
 }
 
+/* Recursively call the destructors of this node and all child nodes.
+ * Deconstructs the parent before its children. */
 static void
-deconstructNode(UA_Server *server, UA_Session *session,
-                const UA_Node *node) {
+recursiveDeconstructNode(UA_Server *server, UA_Session *session,
+                         const UA_Node *node) {
+    /* Was the constructor called for the node? */
+    if(!node->constructed)
+        return;
+
     /* Call the type-level destructor */
     void *context = node->context; /* No longer needed after this function */
     if(node->nodeClass == UA_NODECLASS_OBJECT ||
@@ -1153,15 +1239,11 @@ deconstructNode(UA_Server *server, UA_Session *session,
         server->config.nodeLifecycle.destructor(server, &session->sessionId,
                                                 session->sessionHandle,
                                                 &node->nodeId, context);
-}
 
-static void
-deleteNodeOperation(UA_Server *server, UA_Session *session, void *context,
-                    const UA_DeleteNodesItem *item, UA_StatusCode *result);
+    /* Set the constructed flag to false */
+    UA_Server_editNode(server, &server->adminSession, &node->nodeId,
+                       (UA_EditNodeCallback)setDeconstructedNode, context);
 
-static void
-removeChildren(UA_Server *server, UA_Session *session,
-               const UA_Node *node) {
     /* Browse to get all children of the node */
     UA_BrowseDescription bd;
     UA_BrowseDescription_init(&bd);
@@ -1169,8 +1251,6 @@ removeChildren(UA_Server *server, UA_Session *session,
     bd.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_AGGREGATES);
     bd.includeSubtypes = true;
     bd.browseDirection = UA_BROWSEDIRECTION_FORWARD;
-    bd.nodeClassMask = UA_NODECLASS_OBJECT | UA_NODECLASS_VARIABLE | UA_NODECLASS_METHOD;
-    bd.resultMask = UA_BROWSERESULTMASK_NONE;
 
     UA_BrowseResult br;
     UA_BrowseResult_init(&br);
@@ -1179,35 +1259,55 @@ removeChildren(UA_Server *server, UA_Session *session,
     if(br.statusCode != UA_STATUSCODE_GOOD)
         return;
 
-    UA_DeleteNodesItem item;
-    item.deleteTargetReferences = true;
-
-    /* Remove every child */
+    /* Deconstruct every child node */
     for(size_t i = 0; i < br.referencesSize; ++i) {
         UA_ReferenceDescription *rd = &br.references[i];
-        // check for self-reference to avoid endless loop
-        if(UA_NodeId_equal(&node->nodeId, &rd->nodeId.nodeId))
+        const UA_Node *child = UA_Nodestore_get(server, &rd->nodeId.nodeId);
+        if(!child)
             continue;
-        item.nodeId = rd->nodeId.nodeId;
-        UA_StatusCode retval;
-        deleteNodeOperation(server, session, NULL, &item, &retval);
+        recursiveDeconstructNode(server, session, child);
+        UA_Nodestore_release(server, child);
     }
 
     UA_BrowseResult_deleteMembers(&br);
 }
 
 static void
-removeDeconstructedNode(UA_Server *server, UA_Session *session,
-                        const UA_Node *node, UA_Boolean removeTargetRefs) {
-    /* Remove all children of the node */
-    removeChildren(server, session, node);
+recursiveDeleteNode(UA_Server *server, UA_Session *session,
+                    const UA_Node *node, UA_Boolean removeTargetRefs) {
+    /* Browse to get all children of the node */
+    UA_BrowseDescription bd;
+    UA_BrowseDescription_init(&bd);
+    bd.nodeId = node->nodeId;
+    bd.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_AGGREGATES);
+    bd.includeSubtypes = true;
+    bd.browseDirection = UA_BROWSEDIRECTION_FORWARD;
+
+    UA_BrowseResult br;
+    UA_BrowseResult_init(&br);
+    UA_UInt32 maxrefs = 0;
+    Operation_Browse(server, session, &maxrefs, &bd, &br);
+    if(br.statusCode != UA_STATUSCODE_GOOD)
+        return;
+
+    /* Remove every child */
+    for(size_t i = 0; i < br.referencesSize; ++i) {
+        UA_ReferenceDescription *rd = &br.references[i];
+        /* Check for self-reference to avoid endless loop */
+        if(UA_NodeId_equal(&node->nodeId, &rd->nodeId.nodeId))
+            continue;
+        const UA_Node *child = UA_Nodestore_get(server, &rd->nodeId.nodeId);
+        if(child) {
+            recursiveDeleteNode(server, session, child, true);
+            UA_Nodestore_release(server, child);
+        }
+    }
+
+    UA_BrowseResult_deleteMembers(&br);
 
-    /* Remove references to the node (not the references going out, as the node
-     * will be deleted anyway) */
     if(removeTargetRefs)
         removeIncomingReferences(server, session, node);
 
-    /* Remove the node in the nodestore */
     UA_Nodestore_remove(server, &node->nodeId);
 }
 
@@ -1240,8 +1340,8 @@ deleteNodeOperation(UA_Server *server, UA_Session *session, void *context,
     /* TODO: Check if the information model consistency is violated */
     /* TODO: Check if the node is a mandatory child of a parent */
 
-    deconstructNode(server, session, node);
-    removeDeconstructedNode(server, session, node, item->deleteTargetReferences);
+    recursiveDeconstructNode(server, session, node);
+    recursiveDeleteNode(server, session, node, item->deleteTargetReferences);
     UA_Nodestore_release(server, node);
 }
 
@@ -1258,8 +1358,10 @@ void Service_DeleteNodes(UA_Server *server, UA_Session *session,
     }
 
     response->responseHeader.serviceResult =
-        UA_Server_processServiceOperations(server, session, (UA_ServiceOperation)deleteNodeOperation, NULL,
-                                           &request->nodesToDeleteSize, &UA_TYPES[UA_TYPES_DELETENODESITEM],
+        UA_Server_processServiceOperations(server, session,
+                                           (UA_ServiceOperation)deleteNodeOperation,
+                                           NULL, &request->nodesToDeleteSize,
+                                           &UA_TYPES[UA_TYPES_DELETENODESITEM],
                                            &response->resultsSize, &UA_TYPES[UA_TYPES_STATUSCODE]);
 }