Преглед изворни кода

Use the nodestore plugin in the server

Julian Grothoff пре 7 година
родитељ
комит
b9673ee0bb

+ 25 - 27
CMakeLists.txt

@@ -152,7 +152,7 @@ else()
     endif()
 endif()
 if(UA_ENABLE_MULTITHREADING)
-  list(APPEND open62541_LIBRARIES urcu-cds urcu urcu-common)
+  list(APPEND open62541_LIBRARIES urcu-cds urcu-bp urcu-common)
 endif(UA_ENABLE_MULTITHREADING)
 
 if(NOT UA_COMPILE_AS_CXX AND (CMAKE_COMPILER_IS_GNUCC OR "x${CMAKE_C_COMPILER_ID}" STREQUAL "xClang"))
@@ -256,13 +256,10 @@ set(exported_headers ${PROJECT_BINARY_DIR}/src_generated/ua_config.h
                      ${PROJECT_SOURCE_DIR}/include/ua_plugin_log.h
                      ${PROJECT_SOURCE_DIR}/include/ua_plugin_access_control.h
                      ${PROJECT_SOURCE_DIR}/include/ua_plugin_securitypolicy.h
+                     ${PROJECT_SOURCE_DIR}/include/ua_plugin_nodestore.h
                      ${PROJECT_SOURCE_DIR}/include/ua_server_config.h
                      ${PROJECT_SOURCE_DIR}/include/ua_client.h
-                     ${PROJECT_SOURCE_DIR}/include/ua_client_highlevel.h
-                     ${PROJECT_SOURCE_DIR}/plugins/ua_network_tcp.h
-                     ${PROJECT_SOURCE_DIR}/plugins/ua_accesscontrol_default.h
-                     ${PROJECT_SOURCE_DIR}/plugins/ua_log_stdout.h
-                     ${PROJECT_SOURCE_DIR}/plugins/ua_config_default.h)
+                     ${PROJECT_SOURCE_DIR}/include/ua_client_highlevel.h)
 
 set(internal_headers ${PROJECT_SOURCE_DIR}/deps/queue.h
                      ${PROJECT_SOURCE_DIR}/deps/pcg_basic.h
@@ -275,11 +272,9 @@ set(internal_headers ${PROJECT_SOURCE_DIR}/deps/queue.h
                      ${PROJECT_BINARY_DIR}/src_generated/ua_transport_generated_encoding_binary.h
                      ${PROJECT_SOURCE_DIR}/src/ua_connection_internal.h
                      ${PROJECT_SOURCE_DIR}/src/ua_securechannel.h
-                     ${PROJECT_SOURCE_DIR}/src/server/ua_nodes.h
                      ${PROJECT_SOURCE_DIR}/src/ua_session.h
                      ${PROJECT_SOURCE_DIR}/src/ua_timer.h
                      ${PROJECT_SOURCE_DIR}/src/server/ua_subscription.h
-                     ${PROJECT_SOURCE_DIR}/src/server/ua_nodestore.h
                      ${PROJECT_SOURCE_DIR}/src/server/ua_session_manager.h
                      ${PROJECT_SOURCE_DIR}/src/server/ua_securechannel_manager.h
                      ${PROJECT_SOURCE_DIR}/src/server/ua_server_internal.h
@@ -293,36 +288,31 @@ set(lib_sources ${PROJECT_SOURCE_DIR}/src/ua_types.c
                 ${PROJECT_BINARY_DIR}/src_generated/ua_transport_generated.c
                 ${PROJECT_BINARY_DIR}/src_generated/ua_statuscode_descriptions.c
                 ${PROJECT_SOURCE_DIR}/src/ua_util.c
+                ${PROJECT_SOURCE_DIR}/src/ua_timer.c
+                ${PROJECT_SOURCE_DIR}/src/ua_session.c
                 ${PROJECT_SOURCE_DIR}/src/ua_connection.c
                 ${PROJECT_SOURCE_DIR}/src/ua_securechannel.c
-                ${PROJECT_SOURCE_DIR}/src/ua_session.c
-                ${PROJECT_SOURCE_DIR}/src/ua_timer.c
+                ${PROJECT_SOURCE_DIR}/src/server/ua_nodes.c
                 ${PROJECT_SOURCE_DIR}/src/server/ua_server.c
-                ${PROJECT_SOURCE_DIR}/src/server/ua_server_discovery.c
                 ${PROJECT_SOURCE_DIR}/src/server/ua_server_ns0.c
                 ${PROJECT_SOURCE_DIR}/src/server/ua_server_binary.c
                 ${PROJECT_SOURCE_DIR}/src/server/ua_server_utils.c
                 ${PROJECT_SOURCE_DIR}/src/server/ua_server_worker.c
+                ${PROJECT_SOURCE_DIR}/src/server/ua_server_discovery.c
                 ${PROJECT_SOURCE_DIR}/src/server/ua_securechannel_manager.c
                 ${PROJECT_SOURCE_DIR}/src/server/ua_session_manager.c
-                ${PROJECT_SOURCE_DIR}/src/server/ua_nodes.c
-                # nodestores
-                ${PROJECT_SOURCE_DIR}/src/server/ua_nodestore.c
-                ${PROJECT_SOURCE_DIR}/src/server/ua_nodestore_concurrent.c
+                ${PROJECT_SOURCE_DIR}/src/server/ua_subscription.c
+                ${PROJECT_SOURCE_DIR}/src/server/ua_subscription_datachange.c
                 # services
-                ${PROJECT_SOURCE_DIR}/src/server/ua_services_discovery.c
-                ${PROJECT_SOURCE_DIR}/src/server/ua_services_discovery_multicast.c
-                ${PROJECT_SOURCE_DIR}/src/server/ua_services_securechannel.c
-                ${PROJECT_SOURCE_DIR}/src/server/ua_services_session.c
-                ${PROJECT_SOURCE_DIR}/src/server/ua_services_attribute.c
-                ${PROJECT_SOURCE_DIR}/src/server/ua_services_nodemanagement.c
                 ${PROJECT_SOURCE_DIR}/src/server/ua_services_view.c
-                # method call
                 ${PROJECT_SOURCE_DIR}/src/server/ua_services_call.c
-                # subscriptions
-                ${PROJECT_SOURCE_DIR}/src/server/ua_subscription.c
-                ${PROJECT_SOURCE_DIR}/src/server/ua_subscription_datachange.c
+                ${PROJECT_SOURCE_DIR}/src/server/ua_services_session.c
+                ${PROJECT_SOURCE_DIR}/src/server/ua_services_attribute.c
+                ${PROJECT_SOURCE_DIR}/src/server/ua_services_discovery.c
                 ${PROJECT_SOURCE_DIR}/src/server/ua_services_subscription.c
+                ${PROJECT_SOURCE_DIR}/src/server/ua_services_securechannel.c
+                ${PROJECT_SOURCE_DIR}/src/server/ua_services_nodemanagement.c
+                ${PROJECT_SOURCE_DIR}/src/server/ua_services_discovery_multicast.c
                 # client
                 ${PROJECT_SOURCE_DIR}/src/client/ua_client.c
                 ${PROJECT_SOURCE_DIR}/src/client/ua_client_discovery.c
@@ -332,10 +322,17 @@ set(lib_sources ${PROJECT_SOURCE_DIR}/src/ua_types.c
                 ${PROJECT_SOURCE_DIR}/deps/libc_time.c
                 ${PROJECT_SOURCE_DIR}/deps/pcg_basic.c)
 
+set(default_plugin_headers ${PROJECT_SOURCE_DIR}/plugins/ua_network_tcp.h
+                           ${PROJECT_SOURCE_DIR}/plugins/ua_accesscontrol_default.h
+                           ${PROJECT_SOURCE_DIR}/plugins/ua_log_stdout.h
+                           ${PROJECT_SOURCE_DIR}/plugins/ua_nodestore_default.h
+                           ${PROJECT_SOURCE_DIR}/plugins/ua_config_default.h)
+
 set(default_plugin_sources ${PROJECT_SOURCE_DIR}/plugins/ua_network_tcp.c
                            ${PROJECT_SOURCE_DIR}/plugins/ua_clock.c
                            ${PROJECT_SOURCE_DIR}/plugins/ua_log_stdout.c
                            ${PROJECT_SOURCE_DIR}/plugins/ua_accesscontrol_default.c
+                           ${PROJECT_SOURCE_DIR}/plugins/ua_nodestore_default.c
                            ${PROJECT_SOURCE_DIR}/plugins/ua_config_default.c)
 
 if(UA_DEBUG_DUMP_PKGS)
@@ -445,9 +442,10 @@ add_custom_target(open62541-generator-statuscode DEPENDS
 add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/open62541.h
                    PRE_BUILD
                    COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/tools/amalgamate.py
-                           ${OPEN62541_VER_COMMIT} ${CMAKE_CURRENT_BINARY_DIR}/open62541.h ${exported_headers}
+                           ${OPEN62541_VER_COMMIT} ${CMAKE_CURRENT_BINARY_DIR}/open62541.h
+                           ${exported_headers} ${default_plugin_headers}
                    DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/tools/amalgamate.py
-                           ${exported_headers} ${internal_headers})
+                           ${exported_headers} ${default_plugin_headers})
 
 add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/open62541.c
                    PRE_BUILD

+ 3 - 4
doc/CMakeLists.txt

@@ -27,8 +27,7 @@ generate_rst(${PROJECT_SOURCE_DIR}/include/ua_plugin_log.h ${DOC_SRC_DIR}/plugin
 generate_rst(${PROJECT_SOURCE_DIR}/include/ua_plugin_network.h ${DOC_SRC_DIR}/plugin_network.rst)
 generate_rst(${PROJECT_SOURCE_DIR}/include/ua_plugin_access_control.h ${DOC_SRC_DIR}/plugin_access_control.rst)
 generate_rst(${PROJECT_SOURCE_DIR}/src/server/ua_services.h ${DOC_SRC_DIR}/services.rst)
-generate_rst(${PROJECT_SOURCE_DIR}/src/server/ua_nodestore.h ${DOC_SRC_DIR}/nodestore.rst)
-generate_rst(${PROJECT_SOURCE_DIR}/src/server/ua_nodes.h ${DOC_SRC_DIR}/information_modelling.rst)
+generate_rst(${PROJECT_SOURCE_DIR}/include/ua_plugin_nodestore.h ${DOC_SRC_DIR}/nodestore.rst)
 
 # Doc from tutorial code
 generate_rst(${PROJECT_SOURCE_DIR}/examples/tutorial_datatypes.c ${DOC_SRC_DIR}/tutorial_datatypes.rst)
@@ -46,7 +45,7 @@ add_custom_target(doc_latex ${SPHINX_EXECUTABLE}
           ${DOC_SRC_DIR}/server.rst ${DOC_SRC_DIR}/client.rst ${DOC_SRC_DIR}/client_highlevel.rst
           ${DOC_SRC_DIR}/plugin_log.rst ${DOC_SRC_DIR}/plugin_network.rst
           ${DOC_SRC_DIR}/services.rst ${DOC_SRC_DIR}/plugin_access_control.rst
-          ${DOC_SRC_DIR}/nodestore.rst ${DOC_SRC_DIR}/information_modelling.rst
+          ${DOC_SRC_DIR}/nodestore.rst
           ${DOC_SRC_DIR}/tutorial_datatypes.rst
           ${DOC_SRC_DIR}/tutorial_client_firststeps.rst
           ${DOC_SRC_DIR}/tutorial_server_firststeps.rst
@@ -72,7 +71,7 @@ add_custom_target(doc ${SPHINX_EXECUTABLE}
           ${DOC_SRC_DIR}/server.rst ${DOC_SRC_DIR}/client.rst ${DOC_SRC_DIR}/client_highlevel.rst
           ${DOC_SRC_DIR}/plugin_log.rst ${DOC_SRC_DIR}/plugin_network.rst
           ${DOC_SRC_DIR}/services.rst ${DOC_SRC_DIR}/plugin_access_control.rst
-          ${DOC_SRC_DIR}/nodestore.rst ${DOC_SRC_DIR}/information_modelling.rst
+          ${DOC_SRC_DIR}/nodestore.rst
           ${DOC_SRC_DIR}/tutorial_datatypes.rst
           ${DOC_SRC_DIR}/tutorial_client_firststeps.rst
           ${DOC_SRC_DIR}/tutorial_server_firststeps.rst

+ 4 - 1
examples/server_nodeset.c

@@ -2,7 +2,10 @@
  * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. */
 
 #include <signal.h>
-#include "open62541.h"
+#include "ua_server.h"
+#include "ua_plugin_log.h"
+#include "ua_log_stdout.h"
+#include "ua_config_default.h"
 
 /* Files nodeset.h and nodeset.c are created from server_nodeset.xml in the
  * /src_generated directory by CMake */

+ 5 - 0
include/ua_plugin_nodestore.h

@@ -95,18 +95,23 @@ UA_StatusCode UA_EXPORT
 UA_Node_setAttributes(UA_Node *node, const void *attributes,
                       const UA_DataType *attributeType);
 
+/* Reset the destination node and copy the content of the source */
 UA_StatusCode UA_EXPORT
 UA_Node_copy(const UA_Node *src, UA_Node *dst);
 
+/* Add a single reference to the node */
 UA_StatusCode UA_EXPORT
 UA_Node_addReference(UA_Node *node, const UA_AddReferencesItem *item);
 
+/* Delete a single reference from the node */
 UA_StatusCode UA_EXPORT
 UA_Node_deleteReference(UA_Node *node, const UA_DeleteReferencesItem *item);
 
+/* Delete all references of the node */
 void UA_EXPORT
 UA_Node_deleteReferences(UA_Node *node);
 
+/* Remove all malloc'ed members of the node */
 void UA_EXPORT
 UA_Node_deleteMembers(UA_Node *node);
 

+ 4 - 0
include/ua_server_config.h

@@ -14,6 +14,7 @@ extern "C" {
 #include "ua_plugin_network.h"
 #include "ua_plugin_access_control.h"
 #include "ua_plugin_securitypolicy.h"
+#include "ua_plugin_nodestore.h"
 
 /**
  * Server Configuration
@@ -48,6 +49,9 @@ struct UA_ServerConfig {
     size_t customDataTypesSize;
     UA_DataType *customDataTypes;
 
+    /* Nodestore */
+    UA_Nodestore nodestore;
+
     /* Networking */
     size_t networkLayersSize;
     UA_ServerNetworkLayer *networkLayers;

+ 19 - 16
plugins/ua_config_default.c

@@ -5,8 +5,7 @@
 #include "ua_log_stdout.h"
 #include "ua_network_tcp.h"
 #include "ua_accesscontrol_default.h"
-#include "ua_types_generated.h"
-#include "ua_types.h"
+#include "ua_nodestore_default.h"
 
 #define ANONYMOUS_POLICY "open62541-anonymous-policy"
 #define USERNAME_POLICY "open62541-username-policy"
@@ -26,9 +25,9 @@ UA_DURATIONRANGE(UA_Double min, UA_Double max) {
     return range;
 }
 
- /*******************************/
- /* Default Connection Settings */
- /*******************************/
+/*******************************/
+/* Default Connection Settings */
+/*******************************/
 
 const UA_ConnectionConfig UA_ConnectionConfig_default = {
     0, /* .protocolVersion */
@@ -185,11 +184,17 @@ UA_ServerConfig_new_minimal(UA_UInt16 portNumber,
 
     /* --> Finish setting the default static config <-- */
 
+    UA_StatusCode retval = UA_Nodestore_default_new(&conf->nodestore);
+    if(retval != UA_STATUSCODE_GOOD) {
+        UA_ServerConfig_delete(conf);
+        return NULL;
+    }
+
     /* Add a network layer */
     conf->networkLayers = (UA_ServerNetworkLayer*)
         UA_malloc(sizeof(UA_ServerNetworkLayer));
     if(!conf->networkLayers) {
-        UA_free(conf);
+        UA_ServerConfig_delete(conf);
         return NULL;
     }
     conf->networkLayers[0] =
@@ -199,22 +204,16 @@ UA_ServerConfig_new_minimal(UA_UInt16 portNumber,
     /* Allocate the endpoint */
     conf->endpoints.endpoints = (UA_Endpoint*)UA_malloc(sizeof(UA_Endpoint));
     if(!conf->endpoints.endpoints) {
-        conf->networkLayers[0].deleteMembers(&conf->networkLayers[0]);
-        UA_free(conf->networkLayers);
-        UA_free(conf);
+        UA_ServerConfig_delete(conf);
         return NULL;
     }
     conf->endpoints.count = 1;
 
     /* Populate the endpoint */
-    UA_StatusCode retval =
-        createSecurityPolicyNoneEndpoint(conf, &conf->endpoints.endpoints[0],
-                                         certificate);
+    retval = createSecurityPolicyNoneEndpoint(conf, &conf->endpoints.endpoints[0],
+                                              certificate);
     if(retval != UA_STATUSCODE_GOOD) {
-        conf->networkLayers[0].deleteMembers(&conf->networkLayers[0]);
-        UA_free(conf->networkLayers);
-        UA_free(conf->endpoints.endpoints);
-        UA_free(conf);
+        UA_ServerConfig_delete(conf);
         return NULL;
     }
 
@@ -237,6 +236,10 @@ UA_ServerConfig_delete(UA_ServerConfig *config) {
     /* config->serverCapabilitiesSize = 0; */
 #endif
 
+    /* Nodestore */
+    if(config->nodestore.deleteNodestore)
+        config->nodestore.deleteNodestore(config->nodestore.context);
+
     /* Custom DataTypes */
     for(size_t i = 0; i < config->customDataTypesSize; ++i)
         UA_free(config->customDataTypes[i].members);

+ 41 - 58
src/server/ua_nodes.c

@@ -3,12 +3,11 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ua_server_internal.h"
-#include "ua_nodes.h"
 
 /* There is no UA_Node_new() method here. Creating nodes is part of the
  * NodeStore layer */
 
-void UA_Node_deleteMembersAnyNodeClass(UA_Node *node) {
+void UA_Node_deleteMembers(UA_Node *node) {
     /* Delete standard content */
     UA_NodeId_deleteMembers(&node->nodeId);
     UA_QualifiedName_deleteMembers(&node->browseName);
@@ -134,7 +133,8 @@ UA_ViewNode_copy(const UA_ViewNode *src, UA_ViewNode *dst) {
     return UA_STATUSCODE_GOOD;
 }
 
-UA_StatusCode UA_Node_copyAnyNodeClass(const UA_Node *src, UA_Node *dst) {
+UA_StatusCode
+UA_Node_copy(const UA_Node *src, UA_Node *dst) {
     if(src->nodeClass != dst->nodeClass)
         return UA_STATUSCODE_BADINTERNALERROR;
     
@@ -148,18 +148,17 @@ UA_StatusCode UA_Node_copyAnyNodeClass(const UA_Node *src, UA_Node *dst) {
     dst->context = src->context;
     dst->constructed = src->constructed;
     if(retval != UA_STATUSCODE_GOOD) {
-        UA_Node_deleteMembersAnyNodeClass(dst);
+        UA_Node_deleteMembers(dst);
         return retval;
     }
 
     /* Copy the references */
     dst->references = NULL;
     if(src->referencesSize > 0) {
-        dst->references =
-            (UA_NodeReferenceKind*)UA_calloc(src->referencesSize,
-                                             sizeof(UA_NodeReferenceKind));
+        dst->references = (UA_NodeReferenceKind*)
+            UA_calloc(src->referencesSize, sizeof(UA_NodeReferenceKind));
         if(!dst->references) {
-            UA_Node_deleteMembersAnyNodeClass(dst);
+            UA_Node_deleteMembers(dst);
             return UA_STATUSCODE_BADOUTOFMEMORY;
         }
         dst->referencesSize = src->referencesSize;
@@ -179,7 +178,7 @@ UA_StatusCode UA_Node_copyAnyNodeClass(const UA_Node *src, UA_Node *dst) {
             drefs->targetIdsSize = srefs->targetIdsSize;
         }
         if(retval != UA_STATUSCODE_GOOD) {
-            UA_Node_deleteMembersAnyNodeClass(dst);
+            UA_Node_deleteMembers(dst);
             return retval;
         }
     }
@@ -215,7 +214,7 @@ UA_StatusCode UA_Node_copyAnyNodeClass(const UA_Node *src, UA_Node *dst) {
     }
 
     if(retval != UA_STATUSCODE_GOOD)
-        UA_Node_deleteMembersAnyNodeClass(dst);
+        UA_Node_deleteMembers(dst);
 
     return retval;
 }
@@ -225,12 +224,11 @@ UA_StatusCode UA_Node_copyAnyNodeClass(const UA_Node *src, UA_Node *dst) {
 /******************************/
 
 static UA_StatusCode
-copyStandardAttributes(UA_Node *node, const UA_AddNodesItem *item,
-                       const UA_NodeAttributes *attr) {
-    UA_StatusCode retval;
-    retval  = UA_NodeId_copy(&item->requestedNewNodeId.nodeId, &node->nodeId);
-    retval |= UA_QualifiedName_copy(&item->browseName, &node->browseName);
-    retval |= UA_LocalizedText_copy(&attr->displayName, &node->displayName);
+copyStandardAttributes(UA_Node *node, const UA_NodeAttributes *attr) {
+    /* retval  = UA_NodeId_copy(&item->requestedNewNodeId.nodeId, &node->nodeId); */
+    /* retval |= UA_QualifiedName_copy(&item->browseName, &node->browseName); */
+    UA_StatusCode retval = UA_LocalizedText_copy(&attr->displayName,
+                                                 &node->displayName);
     retval |= UA_LocalizedText_copy(&attr->description, &node->description);
     node->writeMask = attr->writeMask;
     return retval;
@@ -318,72 +316,58 @@ copyMethodNodeAttributes(UA_MethodNode *mnode,
     return UA_STATUSCODE_GOOD;
 }
 
-#define CHECK_ATTRIBUTES(TYPE)                                          \
-    if(item->nodeAttributes.content.decoded.type != &UA_TYPES[UA_TYPES_##TYPE]) { \
-        retval = UA_STATUSCODE_BADNODEATTRIBUTESINVALID;                \
-        break;                                                          \
+#define CHECK_ATTRIBUTES(TYPE)                           \
+    if(attributeType != &UA_TYPES[UA_TYPES_##TYPE]) {    \
+        retval = UA_STATUSCODE_BADNODEATTRIBUTESINVALID; \
+        break;                                           \
     }
 
-/* Copy the attributes into a new node. On success, newNode points to the
- * created node */
 UA_StatusCode
-UA_Node_createFromAttributes(const UA_AddNodesItem *item, UA_Node **newNode) {
-    /* Check that we can read the attributes */
-    if(item->nodeAttributes.encoding < UA_EXTENSIONOBJECT_DECODED ||
-       !item->nodeAttributes.content.decoded.type)
-        return UA_STATUSCODE_BADNODEATTRIBUTESINVALID;
-
-    /* Create the node */
-    // todo: error case where the nodeclass is faulty should return a different
-    // status code
-    UA_Node *node = UA_NodeStore_newNode(item->nodeClass);
-    if(!node)
-        return UA_STATUSCODE_BADOUTOFMEMORY;
+UA_Node_setAttributes(UA_Node *node, const void *attributes,
+                      const UA_DataType *attributeType) {
 
     /* Copy the attributes into the node */
-    void *data = item->nodeAttributes.content.decoded.data;
-    UA_StatusCode retval = copyStandardAttributes(node, item,
-                                                  (const UA_NodeAttributes*)data);
-    switch(item->nodeClass) {
+    UA_StatusCode retval = UA_STATUSCODE_GOOD;
+    switch(node->nodeClass) {
     case UA_NODECLASS_OBJECT:
         CHECK_ATTRIBUTES(OBJECTATTRIBUTES);
-        retval |= copyObjectNodeAttributes((UA_ObjectNode*)node,
-                                           (const UA_ObjectAttributes*)data);
+        retval = copyObjectNodeAttributes((UA_ObjectNode*)node,
+                                          (const UA_ObjectAttributes*)attributes);
         break;
     case UA_NODECLASS_VARIABLE:
         CHECK_ATTRIBUTES(VARIABLEATTRIBUTES);
-        retval |= copyVariableNodeAttributes((UA_VariableNode*)node,
-                                             (const UA_VariableAttributes*)data);
+        retval = copyVariableNodeAttributes((UA_VariableNode*)node,
+                                            (const UA_VariableAttributes*)attributes);
         break;
     case UA_NODECLASS_OBJECTTYPE:
         CHECK_ATTRIBUTES(OBJECTTYPEATTRIBUTES);
-        retval |= copyObjectTypeNodeAttributes((UA_ObjectTypeNode*)node,
-                                               (const UA_ObjectTypeAttributes*)data);
+        retval = copyObjectTypeNodeAttributes((UA_ObjectTypeNode*)node,
+                                              (const UA_ObjectTypeAttributes*)attributes);
         break;
     case UA_NODECLASS_VARIABLETYPE:
         CHECK_ATTRIBUTES(VARIABLETYPEATTRIBUTES);
-        retval |= copyVariableTypeNodeAttributes((UA_VariableTypeNode*)node,
-                                                 (const UA_VariableTypeAttributes*)data);
+        retval = copyVariableTypeNodeAttributes((UA_VariableTypeNode*)node,
+                                                (const UA_VariableTypeAttributes*)attributes);
         break;
     case UA_NODECLASS_REFERENCETYPE:
         CHECK_ATTRIBUTES(REFERENCETYPEATTRIBUTES);
-        retval |= copyReferenceTypeNodeAttributes((UA_ReferenceTypeNode*)node,
-                                                  (const UA_ReferenceTypeAttributes*)data);
+        retval = copyReferenceTypeNodeAttributes((UA_ReferenceTypeNode*)node,
+                                                 (const UA_ReferenceTypeAttributes*)attributes);
         break;
     case UA_NODECLASS_DATATYPE:
         CHECK_ATTRIBUTES(DATATYPEATTRIBUTES);
-        retval |= copyDataTypeNodeAttributes((UA_DataTypeNode*)node,
-                                             (const UA_DataTypeAttributes*)data);
+        retval = copyDataTypeNodeAttributes((UA_DataTypeNode*)node,
+                                            (const UA_DataTypeAttributes*)attributes);
         break;
     case UA_NODECLASS_VIEW:
         CHECK_ATTRIBUTES(VIEWATTRIBUTES);
-        retval |= copyViewNodeAttributes((UA_ViewNode*)node,
-                                         (const UA_ViewAttributes*)data);
+        retval = copyViewNodeAttributes((UA_ViewNode*)node,
+                                        (const UA_ViewAttributes*)attributes);
         break;
     case UA_NODECLASS_METHOD:
         CHECK_ATTRIBUTES(METHODATTRIBUTES);
-        retval |= copyMethodNodeAttributes((UA_MethodNode*)node,
-                                           (const UA_MethodAttributes*)data);
+        retval = copyMethodNodeAttributes((UA_MethodNode*)node,
+                                          (const UA_MethodAttributes*)attributes);
         break;
     case UA_NODECLASS_UNSPECIFIED:
     default:
@@ -391,10 +375,9 @@ UA_Node_createFromAttributes(const UA_AddNodesItem *item, UA_Node **newNode) {
     }
 
     if(retval == UA_STATUSCODE_GOOD)
-        *newNode = (UA_Node*)node;
-    else
-        UA_NodeStore_deleteNode(node);
-
+        retval = copyStandardAttributes(node, (const UA_NodeAttributes*)attributes);
+    if(retval != UA_STATUSCODE_GOOD)
+        UA_Node_deleteMembers(node);
     return retval;
 }
 

+ 6 - 16
src/server/ua_server.c

@@ -9,10 +9,6 @@
 #include "ua_namespaceinit_generated.h"
 #endif
 
-#if defined(UA_ENABLE_MULTITHREADING) && !defined(NDEBUG)
-UA_THREAD_LOCAL bool rcu_locked = false;
-#endif
-
 /**********************/
 /* Namespace Handling */
 /**********************/
@@ -52,12 +48,11 @@ UA_UInt16 UA_Server_addNamespace(UA_Server *server, const char* name) {
 UA_StatusCode
 UA_Server_forEachChildNodeCall(UA_Server *server, UA_NodeId parentNodeId,
                                UA_NodeIteratorCallback callback, void *handle) {
-    UA_RCU_LOCK();
-    const UA_Node *parent = UA_NodeStore_get(server->nodestore, &parentNodeId);
-    if(!parent) {
-        UA_RCU_UNLOCK();
+    const UA_Node *parent =
+        server->config.nodestore.getNode(server->config.nodestore.context,
+                                         &parentNodeId);
+    if(!parent)
         return UA_STATUSCODE_BADNODEIDINVALID;
-    }
 
     /* TODO: We need to do an ugly copy of the references array since users may
      * delete references from within the callback. In single-threaded mode this
@@ -68,7 +63,7 @@ UA_Server_forEachChildNodeCall(UA_Server *server, UA_NodeId parentNodeId,
     UA_StatusCode retval = UA_Array_copy(parent->references, parent->referencesSize,
         (void**)&refs, &UA_TYPES[UA_TYPES_REFERENCENODE]);
     if(retval != UA_STATUSCODE_GOOD) {
-        UA_RCU_UNLOCK();
+        server->config.nodestore.releaseNode(server->config.nodestore.context, parent);
         return retval;
     }
 
@@ -77,8 +72,8 @@ UA_Server_forEachChildNodeCall(UA_Server *server, UA_NodeId parentNodeId,
         retval |= callback(ref->targetId.nodeId, ref->isInverse,
                            ref->referenceTypeId, handle);
     }
-    UA_RCU_UNLOCK();
 
+    server->config.nodestore.releaseNode(server->config.nodestore.context, parent);
     UA_Array_delete(refs, refssize, &UA_TYPES[UA_TYPES_REFERENCENODE]);
     return retval;
 }
@@ -92,9 +87,6 @@ void UA_Server_delete(UA_Server *server) {
     /* Delete all internal data */
     UA_SecureChannelManager_deleteMembers(&server->secureChannelManager);
     UA_SessionManager_deleteMembers(&server->sessionManager);
-    UA_RCU_LOCK();
-    UA_NodeStore_delete(server->nodestore);
-    UA_RCU_UNLOCK();
     UA_Array_delete(server->namespaces, server->namespacesSize, &UA_TYPES[UA_TYPES_STRING]);
 
 #ifdef UA_ENABLE_DISCOVERY
@@ -179,7 +171,6 @@ UA_Server_new(const UA_ServerConfig *config) {
 
     server->config = *config;
     server->startTime = UA_DateTime_now();
-    server->nodestore = UA_NodeStore_new();
 
     /* Set a seed for non-cyptographic randomness */
 #ifndef UA_ENABLE_DETERMINISTIC_RNG
@@ -196,7 +187,6 @@ UA_Server_new(const UA_ServerConfig *config) {
 
     /* Initialized the dispatch queue for worker threads */
 #ifdef UA_ENABLE_MULTITHREADING
-    rcu_init();
     cds_wfcq_init(&server->dispatchQueue_head, &server->dispatchQueue_tail);
 #endif
 

+ 89 - 190
src/server/ua_server_internal.h

@@ -16,50 +16,17 @@ extern "C" {
 #include "ua_connection_internal.h"
 #include "ua_session_manager.h"
 #include "ua_securechannel_manager.h"
-#include "ua_nodestore.h"
 
-#ifdef UA_ENABLE_DISCOVERY_MULTICAST
-#include "mdnsd/libmdnsd/mdnsd.h"
-#endif
-
-/* The general idea of RCU is to delay freeing nodes (or any callback invoked
- * with call_rcu) until all threads have left their critical section. Thus we
- * can delete nodes safely in concurrent operations. The macros UA_RCU_LOCK and
- * UA_RCU_UNLOCK are used to test during debugging that we do not nest read-side
- * critical sections (although this is generally allowed). */
 #ifdef UA_ENABLE_MULTITHREADING
-# define _LGPL_SOURCE
-# include <urcu.h>
-# include <urcu/lfstack.h>
-# ifdef NDEBUG
-#  define UA_RCU_LOCK() rcu_read_lock()
-#  define UA_RCU_UNLOCK() rcu_read_unlock()
-#  define UA_ASSERT_RCU_LOCKED()
-#  define UA_ASSERT_RCU_UNLOCKED()
-# else
-   extern UA_THREAD_LOCAL bool rcu_locked;
-#  define UA_ASSERT_RCU_LOCKED() assert(rcu_locked)
-#  define UA_ASSERT_RCU_UNLOCKED() assert(!rcu_locked)
-#  define UA_RCU_LOCK() do {                      \
-        UA_ASSERT_RCU_UNLOCKED();                 \
-        rcu_locked = true;                        \
-        rcu_read_lock(); } while(0)
-#  define UA_RCU_UNLOCK() do {                    \
-        UA_ASSERT_RCU_LOCKED();                   \
-        rcu_locked = false;                       \
-        rcu_read_unlock(); } while(0)
-# endif
-#else
-# define UA_RCU_LOCK()
-# define UA_RCU_UNLOCK()
-# define UA_ASSERT_RCU_LOCKED()
-# define UA_ASSERT_RCU_UNLOCKED()
-#endif
 
-#ifdef UA_ENABLE_MULTITHREADING
+/* TODO: Don't depend on liburcu */
+#include <urcu.h>
+#include <urcu/lfstack.h>
+
 struct UA_Worker;
 typedef struct UA_Worker UA_Worker;
-#endif
+
+#endif /* UA_ENABLE_MULTITHREADING */
 
 #ifdef UA_ENABLE_DISCOVERY
 
@@ -69,7 +36,15 @@ typedef struct registeredServer_list_entry {
     UA_DateTime lastSeen;
 } registeredServer_list_entry;
 
-# ifdef UA_ENABLE_DISCOVERY_MULTICAST
+typedef struct periodicServerRegisterCallback_entry {
+    LIST_ENTRY(periodicServerRegisterCallback_entry) pointers;
+    struct PeriodicServerRegisterCallback *callback;
+} periodicServerRegisterCallback_entry;
+
+#ifdef UA_ENABLE_DISCOVERY_MULTICAST
+
+#include "mdnsd/libmdnsd/mdnsd.h"
+
 typedef struct serverOnNetwork_list_entry {
     LIST_ENTRY(serverOnNetwork_list_entry) pointers;
     UA_ServerOnNetwork serverOnNetwork;
@@ -87,12 +62,6 @@ typedef struct serverOnNetwork_hash_entry {
 } serverOnNetwork_hash_entry;
 
 #endif /* UA_ENABLE_DISCOVERY_MULTICAST */
-
-typedef struct periodicServerRegisterCallback_entry {
-    LIST_ENTRY(periodicServerRegisterCallback_entry) pointers;
-    struct PeriodicServerRegisterCallback *callback;
-} periodicServerRegisterCallback_entry;
-
 #endif /* UA_ENABLE_DISCOVERY */
 
 struct UA_Server {
@@ -103,9 +72,6 @@ struct UA_Server {
     UA_SecureChannelManager secureChannelManager;
     UA_SessionManager sessionManager;
 
-    /* Address Space */
-    UA_NodeStore *nodestore;
-
 #ifdef UA_ENABLE_DISCOVERY
     /* Discovery */
     LIST_HEAD(registeredServer_list, registeredServer_list_entry) registeredServers; // doubly-linked list of registered servers
@@ -135,6 +101,7 @@ struct UA_Server {
 # endif
 #endif
 
+    /* Namespaces */
     size_t namespacesSize;
     UA_String *namespaces;
 
@@ -154,8 +121,7 @@ struct UA_Server {
     struct cds_wfcq_tail dispatchQueue_tail; /* Dispatch queue tail for the worker threads */
 #endif
 
-    /* Config is the last element so that MSVC allows the usernamePasswordLogins
-     * field with zero-sized array */
+    /* Config */
     UA_ServerConfig config;
 };
 
@@ -163,21 +129,36 @@ struct UA_Server {
 /* Node Handling */
 /*****************/
 
-UA_StatusCode UA_Node_copyAnyNodeClass(const UA_Node *src, UA_Node *dst);
-void UA_Node_deleteMembersAnyNodeClass(UA_Node *node);
+#define UA_Nodestore_get(SERVER, NODEID)                                \
+    (SERVER)->config.nodestore.getNode((SERVER)->config.nodestore.context, NODEID)
 
-UA_StatusCode UA_Node_addReference(UA_Node *node, const UA_AddReferencesItem *item);
-UA_StatusCode UA_Node_deleteReference(UA_Node *node, const UA_DeleteReferencesItem *item);
-void UA_Node_deleteReferences(UA_Node *node);
+#define UA_Nodestore_release(SERVER, NODEID)                            \
+    (SERVER)->config.nodestore.releaseNode((SERVER)->config.nodestore.context, NODEID)
 
-UA_StatusCode
-UA_Node_createFromAttributes(const UA_AddNodesItem *item, UA_Node **newNode);
+#define UA_Nodestore_new(SERVER, NODECLASS)                               \
+    (SERVER)->config.nodestore.newNode((SERVER)->config.nodestore.context, NODECLASS)
+
+#define UA_Nodestore_getCopy(SERVER, NODEID, OUTNODE)                   \
+    (SERVER)->config.nodestore.getNodeCopy((SERVER)->config.nodestore.context, NODEID, OUTNODE)
+
+#define UA_Nodestore_insert(SERVER, NODE, OUTNODEID)                    \
+    (SERVER)->config.nodestore.insertNode((SERVER)->config.nodestore.context, NODE, OUTNODEID)
+
+#define UA_Nodestore_delete(SERVER, NODE)                               \
+    (SERVER)->config.nodestore.deleteNode((SERVER)->config.nodestore.context, NODE)
+
+#define UA_Nodestore_remove(SERVER, NODEID)                             \
+    (SERVER)->config.nodestore.removeNode((SERVER)->config.nodestore.context, NODEID)
 
-/* Calls callback on the node. In the multithreaded case, the node is copied before and replaced in
-   the nodestore. */
-typedef UA_StatusCode (*UA_EditNodeCallback)(UA_Server*, UA_Session*, UA_Node*, const void*);
-UA_StatusCode UA_Server_editNode(UA_Server *server, UA_Session *session, const UA_NodeId *nodeId,
-                                 UA_EditNodeCallback callback, const void *data);
+/* Calls the callback with the node retrieved from the nodestore on top of the
+ * stack. Either a copy or the original node for in-situ editing. Depends on
+ * multithreading and the nodestore.*/
+typedef UA_StatusCode (*UA_EditNodeCallback)(UA_Server*, UA_Session*,
+                                             UA_Node *node, const void*);
+UA_StatusCode UA_Server_editNode(UA_Server *server, UA_Session *session,
+                                 const UA_NodeId *nodeId,
+                                 UA_EditNodeCallback callback,
+                                 const void *data);
 
 /*************/
 /* Callbacks */
@@ -188,6 +169,8 @@ UA_StatusCode UA_Server_editNode(UA_Server *server, UA_Session *session, const U
 UA_StatusCode
 UA_Server_delayedCallback(UA_Server *server, UA_ServerCallback callback, void *data);
 
+/* Callback is executed in the same thread or, if possible, dispatched to one of
+ * the worker threads. */
 void
 UA_Server_workerCallback(UA_Server *server, UA_ServerCallback callback, void *data);
 
@@ -206,15 +189,9 @@ UA_UInt16 addNamespace(UA_Server *server, const UA_String name);
 UA_Boolean
 UA_Node_hasSubTypeOrInstances(const UA_Node *node);
 
-const UA_VariableTypeNode *
-getVariableNodeType(UA_Server *server, const UA_VariableNode *node);
-
-const UA_ObjectTypeNode *
-getObjectNodeType(UA_Server *server, const UA_ObjectNode *node);
-
 /* Recursively searches "upwards" in the tree following specific reference types */
 UA_Boolean
-isNodeInTree(UA_NodeStore *ns, const UA_NodeId *leafNode,
+isNodeInTree(UA_Nodestore *ns, const UA_NodeId *leafNode,
              const UA_NodeId *nodeToFind, const UA_NodeId *referenceTypeIds,
              size_t referenceTypeIdsSize);
 
@@ -223,15 +200,18 @@ isNodeInTree(UA_NodeStore *ns, const UA_NodeId *leafNode,
  * ``hasSubType`` references. Since multiple-inheritance is possible in general,
  * duplicate entries are removed. */
 UA_StatusCode
-getTypeHierarchy(UA_NodeStore *ns, const UA_NodeId *leafType,
+getTypeHierarchy(UA_Nodestore *ns, const UA_NodeId *leafType,
                  UA_NodeId **typeHierarchy, size_t *typeHierarchySize);
 
-/* Returns a pointer to the nodeid of the node type in the node's references. If
- * no type is defined, a pointer to UA_NODEID_NULL is returned */
-const UA_NodeId * getNodeType(UA_Server *server, const UA_Node *node);
+/* Returns the type node from the node on the stack top. The type node is pushed
+ * on the stack and returned. */
+const UA_Node * getNodeType(UA_Server *server, const UA_Node *node);
 
+/* Many services come as an array of operations. This function generalizes the
+ * processing of the operations. */
 typedef void (*UA_ServiceOperation)(UA_Server *server, UA_Session *session,
-                                    const void *requestOperation, void *responseOperation);
+                                    const void *requestOperation,
+                                    void *responseOperation);
 
 UA_StatusCode
 UA_Server_processServiceOperations(UA_Server *server, UA_Session *session,
@@ -249,11 +229,18 @@ UA_StatusCode
 readValueAttribute(UA_Server *server, UA_Session *session,
                    const UA_VariableNode *vn, UA_DataValue *v);
 
+/* Test whether the value matches a variable definition given by
+ * - datatype
+ * - valueranke
+ * - array dimensions.
+ * Sometimes it can be necessary to transform the content of the value, e.g.
+ * byte array to bytestring or uint32 to some enum. If editableValue is non-NULL,
+ * we try to create a matching variant that points to the original data. */
 UA_StatusCode
-typeCheckValue(UA_Server *server, const UA_NodeId *targetDataTypeId,
-               UA_Int32 targetValueRank, size_t targetArrayDimensionsSize,
-               const UA_UInt32 *targetArrayDimensions, const UA_Variant *value,
-               const UA_NumericRange *range, UA_Variant *editableValue);
+compatibleValue(UA_Server *server, const UA_NodeId *targetDataTypeId,
+                UA_Int32 targetValueRank, size_t targetArrayDimensionsSize,
+                const UA_UInt32 *targetArrayDimensions, const UA_Variant *value,
+                const UA_NumericRange *range);
 
 UA_StatusCode
 compatibleArrayDimensions(size_t constraintArrayDimensionsSize,
@@ -268,11 +255,6 @@ UA_Boolean
 compatibleDataType(UA_Server *server, const UA_NodeId *dataType,
                    const UA_NodeId *constraintDataType);
 
-UA_StatusCode
-writeValueRankAttribute(UA_Server *server, UA_Session *session,
-                        UA_VariableNode *node, UA_Int32 valueRank,
-                        UA_Int32 constraintValueRank);
-
 UA_StatusCode
 compatibleValueRanks(UA_Int32 valueRank, UA_Int32 constraintValueRank);
 
@@ -338,118 +320,35 @@ UA_Discovery_removeRecord(UA_Server *server, const UA_String *servername,
 /* AddNodes Begin and Finish */
 /*****************************/
 
-/* Don't use this function. There are typed versions as inline functions. */
-UA_StatusCode UA_EXPORT
-__UA_Server_addNode_begin(UA_Server *server, const UA_NodeClass nodeClass,
-                          const UA_NodeId *requestedNewNodeId,
-                          const UA_QualifiedName *browseName,
-                          const UA_NodeAttributes *attr,
-                          const UA_DataType *attributeType,
-                          void *nodeContext, UA_NodeId *outNewNodeId);
-
-/* The inline function UA_Server_addNode_finish might be more convenient to
- * pass NodeIds in-situ (e.g. UA_NODEID_NUMERIC(0, 5)) */
-UA_StatusCode UA_EXPORT
-UA_Server_addNode_finish(UA_Server *server, const UA_NodeId nodeId,
-                         const UA_NodeId parentNodeId,
-                         const UA_NodeId referenceTypeId,
-                         const UA_NodeId typeDefinition);
-
-static UA_INLINE UA_StatusCode
-UA_Server_addReferenceTypeNode_begin(UA_Server *server, const UA_NodeId requestedNewNodeId,
-                                     const UA_QualifiedName browseName,
-                                     const UA_ReferenceTypeAttributes attr,
-                                     void *nodeContext, UA_NodeId *outNewNodeId) {
-    return __UA_Server_addNode_begin(server, UA_NODECLASS_REFERENCETYPE,
-                                     &requestedNewNodeId,
-                                     &browseName, (const UA_NodeAttributes*)&attr,
-                                     &UA_TYPES[UA_TYPES_REFERENCETYPEATTRIBUTES],
-                                     nodeContext, outNewNodeId);
-}
-
-static UA_INLINE UA_StatusCode
-UA_Server_addDataTypeNode_begin(UA_Server *server,
-                                const UA_NodeId requestedNewNodeId,
-                                const UA_QualifiedName browseName,
-                                const UA_DataTypeAttributes attr,
-                                void *nodeContext, UA_NodeId *outNewNodeId) {
-    return __UA_Server_addNode_begin(server, UA_NODECLASS_DATATYPE,
-                                     &requestedNewNodeId,
-                                     &browseName, (const UA_NodeAttributes*)&attr,
-                                     &UA_TYPES[UA_TYPES_DATATYPEATTRIBUTES],
-                                     nodeContext, outNewNodeId);
-}
-
-static UA_INLINE UA_StatusCode
-UA_Server_addVariableNode_begin(UA_Server *server, const UA_NodeId requestedNewNodeId,
-                                const UA_QualifiedName browseName,
-                                const UA_VariableAttributes attr,
-                                void *nodeContext, UA_NodeId *outNewNodeId) {
-    return __UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLE,
-                                     &requestedNewNodeId, &browseName,
-                                     (const UA_NodeAttributes*)&attr,
-                                     &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],
-                                     nodeContext, outNewNodeId);
-}
-
-static UA_INLINE UA_StatusCode
-UA_Server_addVariableTypeNode_begin(UA_Server *server, const UA_NodeId requestedNewNodeId,
-                                    const UA_QualifiedName browseName,
-                                    const UA_VariableTypeAttributes attr,
-                                    void *nodeContext, UA_NodeId *outNewNodeId) {
-    return __UA_Server_addNode_begin(server, UA_NODECLASS_VARIABLETYPE,
-                                     &requestedNewNodeId, &browseName,
-                                     (const UA_NodeAttributes*)&attr,
-                                     &UA_TYPES[UA_TYPES_VARIABLETYPEATTRIBUTES],
-                                     nodeContext, outNewNodeId);
-}
-
-static UA_INLINE UA_StatusCode
-UA_Server_addObjectNode_begin(UA_Server *server, const UA_NodeId requestedNewNodeId,
-                              const UA_QualifiedName browseName,
-                              const UA_ObjectAttributes attr,
-                              void *nodeContext, UA_NodeId *outNewNodeId) {
-    return __UA_Server_addNode_begin(server, UA_NODECLASS_OBJECT,
-                                     &requestedNewNodeId,
-                                     &browseName, (const UA_NodeAttributes*)&attr,
-                                     &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES],
-                                     nodeContext, outNewNodeId);
-}
-
-static UA_INLINE UA_StatusCode
-UA_Server_addObjectTypeNode_begin(UA_Server *server,
-                                  const UA_NodeId requestedNewNodeId,
-                                  const UA_QualifiedName browseName,
-                                  const UA_ObjectTypeAttributes attr,
-                                  void *nodeContext, UA_NodeId *outNewNodeId) {
-    return __UA_Server_addNode_begin(server, UA_NODECLASS_OBJECTTYPE,
-                                     &requestedNewNodeId,
-                                     &browseName, (const UA_NodeAttributes*)&attr,
-                                     &UA_TYPES[UA_TYPES_OBJECTTYPEATTRIBUTES],
-                                     nodeContext, outNewNodeId);
-}
-
-static UA_INLINE UA_StatusCode
-UA_Server_addMethodNode_begin(UA_Server *server, const UA_NodeId requestedNewNodeId,
-                              const UA_QualifiedName browseName,
-                              const UA_MethodAttributes attr,
-                              void *nodeContext, UA_NodeId *outNewNodeId) {
-    return __UA_Server_addNode_begin(server, UA_NODECLASS_METHOD,
-                                     &requestedNewNodeId,
-                                     &browseName, (const UA_NodeAttributes*)&attr,
-                                     &UA_TYPES[UA_TYPES_METHODATTRIBUTES],
-                                     nodeContext, outNewNodeId);
-}
-
-#ifdef UA_ENABLE_METHODCALLS
+/* Creates a new node in the nodestore. */
+UA_StatusCode
+Operation_addNode_begin(UA_Server *server, UA_Session *session,
+                        const UA_AddNodesItem *item, void *nodeContext,
+                        UA_NodeId *outNewNodeId, UA_Boolean overrideChecks);
+
+/* Children, references, type-checking, constructors. */
+UA_StatusCode
+Operation_addNode_finish(UA_Server *server, UA_Session *session,
+                         const UA_NodeId *nodeId, const UA_NodeId *parentNodeId,
+                         const UA_NodeId *referenceTypeId, const UA_NodeId *typeDefinitionId,
+                         UA_Boolean overrideChecks);
+
+/* UA_StatusCode */
+/* UA_Server_addNode_begin(UA_Server *server, const UA_AddNodesItem *item, */
+/*                         void *nodeContext, UA_NodeId *outnewNodeId); */
+
+/* UA_StatusCode */
+/* UA_Server_addNode_finish(UA_Server *server, const UA_NodeId nodeId, */
+/*                          const UA_NodeId parentNodeId, */
+/*                          const UA_NodeId referenceTypeId, */
+/*                          const UA_NodeId typeDefinitionId); */
 
 UA_StatusCode
 UA_Server_addMethodNode_finish(UA_Server *server, const UA_NodeId nodeId,
                                const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId,
-                               UA_MethodCallback method, 
+                               UA_MethodCallback method,
                                size_t inputArgumentsSize, const UA_Argument* inputArguments,
                                size_t outputArgumentsSize, const UA_Argument* outputArguments);
-#endif
 
 /**********************/
 /* Create Namespace 0 */

+ 65 - 59
src/server/ua_server_ns0.c

@@ -176,11 +176,30 @@ readMonitoredItems(UA_Server *server, const UA_NodeId *sessionId,
 /*****************/
 
 static void
-addReferenceInternal(UA_Server *server, UA_UInt32 sourceId, UA_UInt32 refTypeId,
-                     UA_UInt32 targetId, UA_Boolean isForward) {
-    UA_Server_addReference(server, UA_NODEID_NUMERIC(0, sourceId),
-                           UA_NODEID_NUMERIC(0, refTypeId),
-                           UA_EXPANDEDNODEID_NUMERIC(0, targetId), isForward);
+addNode_begin(UA_Server *server, UA_NodeClass nodeClass,
+              UA_UInt32 nodeId, char *name, void *attributes,
+              const UA_DataType *attributesType, UA_Boolean overrideChecks) {
+    UA_AddNodesItem item;
+    UA_AddNodesItem_init(&item);
+    item.nodeClass = nodeClass;
+    item.requestedNewNodeId.nodeId = UA_NODEID_NUMERIC(0, nodeId);
+    item.browseName = UA_QUALIFIEDNAME(0, name);
+    item.nodeAttributes.encoding = UA_EXTENSIONOBJECT_DECODED_NODELETE;
+    item.nodeAttributes.content.decoded.data = attributes;
+    item.nodeAttributes.content.decoded.type = attributesType;
+    Operation_addNode_begin(server, &adminSession, &item, NULL, NULL, overrideChecks);
+}
+
+static void
+addNode_finish(UA_Server *server, UA_UInt32 nodeId,
+               UA_UInt32 parentNodeId, UA_UInt32 referenceTypeId,
+               UA_UInt32 typeDefinitionId, UA_Boolean overrideChecks) {
+    UA_NodeId node = UA_NODEID_NUMERIC(0, nodeId);
+    UA_NodeId parentNode = UA_NODEID_NUMERIC(0, parentNodeId);
+    UA_NodeId referenceType = UA_NODEID_NUMERIC(0, referenceTypeId);
+    UA_NodeId typeDefinition = UA_NODEID_NUMERIC(0, typeDefinitionId);
+    Operation_addNode_finish(server, &adminSession, &node, &parentNode,
+                             &referenceType, &typeDefinition, overrideChecks);
 }
 
 static void
@@ -296,16 +315,16 @@ void UA_Server_createNS0(UA_Server *server) {
     references_attr.isAbstract = true;
     references_attr.symmetric = true;
     references_attr.inverseName = UA_LOCALIZEDTEXT("", "References");
-    UA_Server_addReferenceTypeNode_begin(server, UA_NODEID_NUMERIC(0, UA_NS0ID_REFERENCES),
-                                         UA_QUALIFIEDNAME(0, "References"), references_attr, NULL, NULL);
+    addNode_begin(server, UA_NODECLASS_REFERENCETYPE, UA_NS0ID_REFERENCES, "References",
+                  &references_attr, &UA_TYPES[UA_TYPES_REFERENCETYPEATTRIBUTES], false);
 
     UA_ReferenceTypeAttributes hassubtype_attr = UA_ReferenceTypeAttributes_default;
     hassubtype_attr.displayName = UA_LOCALIZEDTEXT("", "HasSubtype");
     hassubtype_attr.isAbstract = false;
     hassubtype_attr.symmetric = false;
     hassubtype_attr.inverseName = UA_LOCALIZEDTEXT("", "SubtypeOf");
-    UA_Server_addReferenceTypeNode_begin(server, UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE),
-                                         UA_QUALIFIEDNAME(0, "HasSubtype"), hassubtype_attr, NULL, NULL);
+    addNode_begin(server, UA_NODECLASS_REFERENCETYPE, UA_NS0ID_HASSUBTYPE, "HasSubtype",
+                  &hassubtype_attr, &UA_TYPES[UA_TYPES_REFERENCETYPEATTRIBUTES], false);
 
     addReferenceTypeNode(server, "HierarchicalReferences", NULL,
                          UA_NS0ID_HIERARCHICALREFERENCES,
@@ -343,8 +362,8 @@ void UA_Server_createNS0(UA_Server *server) {
                          false, false, UA_NS0ID_HASCHILD);
 
     /* Complete bootstrap of HasSubtype */
-    addReferenceInternal(server, UA_NS0ID_HASCHILD, UA_NS0ID_HASSUBTYPE,
-                         UA_NS0ID_HASSUBTYPE, true);
+    addNode_finish(server, UA_NS0ID_HASSUBTYPE, UA_NS0ID_HASCHILD,
+                   UA_NS0ID_HASSUBTYPE, 0, false);
 
     addReferenceTypeNode(server, "HasProperty", "PropertyOf", UA_NS0ID_HASPROPERTY,
                          false, false, UA_NS0ID_AGGREGATES);
@@ -366,8 +385,8 @@ void UA_Server_createNS0(UA_Server *server) {
     UA_DataTypeAttributes basedatatype_attr = UA_DataTypeAttributes_default;
     basedatatype_attr.displayName = UA_LOCALIZEDTEXT("", "BaseDataType");
     basedatatype_attr.isAbstract = true;
-    UA_Server_addDataTypeNode_begin(server, UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATATYPE),
-                                    UA_QUALIFIEDNAME(0, "BaseDataType"), basedatatype_attr, NULL, NULL);
+    addNode_begin(server, UA_NODECLASS_DATATYPE, UA_NS0ID_BASEDATATYPE, "BaseDataType",
+                  &basedatatype_attr, &UA_TYPES[UA_TYPES_DATATYPEATTRIBUTES], false);
 
     addDataTypeNode(server, "Boolean", UA_NS0ID_BOOLEAN, false, UA_NS0ID_BASEDATATYPE);
     addDataTypeNode(server, "Number", UA_NS0ID_NUMBER, true, UA_NS0ID_BASEDATATYPE);
@@ -412,8 +431,8 @@ void UA_Server_createNS0(UA_Server *server) {
     basevar_attr.isAbstract = true;
     basevar_attr.valueRank = -2;
     basevar_attr.dataType = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATATYPE);
-    UA_Server_addVariableTypeNode_begin(server, UA_NODEID_NUMERIC(0, UA_NS0ID_BASEVARIABLETYPE),
-                                        UA_QUALIFIEDNAME(0, "BaseVariableType"), basevar_attr, NULL, NULL);
+    addNode_begin(server, UA_NODECLASS_VARIABLETYPE, UA_NS0ID_BASEVARIABLETYPE, "BaseVariableType",
+                  &basevar_attr, &UA_TYPES[UA_TYPES_VARIABLETYPEATTRIBUTES], true);
 
     addVariableTypeNode(server, "BaseDataVariableType", UA_NS0ID_BASEDATAVARIABLETYPE,
                         false, -2, UA_NS0ID_BASEDATATYPE, NULL, UA_NS0ID_BASEVARIABLETYPE);
@@ -426,7 +445,8 @@ void UA_Server_createNS0(UA_Server *server) {
                         UA_NS0ID_BASEDATAVARIABLETYPE);
 
     addVariableTypeNode(server, "ServerStatusType", UA_NS0ID_SERVERSTATUSTYPE,
-                        false, -1, UA_NS0ID_SERVERSTATUSDATATYPE, &UA_TYPES[UA_TYPES_SERVERSTATUSDATATYPE],
+                        false, -1, UA_NS0ID_SERVERSTATUSDATATYPE,
+                        &UA_TYPES[UA_TYPES_SERVERSTATUSDATATYPE],
                         UA_NS0ID_BASEDATAVARIABLETYPE);
 
     /***************/
@@ -436,8 +456,8 @@ void UA_Server_createNS0(UA_Server *server) {
     /* Bootstrap BaseObjectType */
     UA_ObjectTypeAttributes baseobj_attr = UA_ObjectTypeAttributes_default;
     baseobj_attr.displayName = UA_LOCALIZEDTEXT("", "BaseObjectType");
-    UA_Server_addObjectTypeNode_begin(server, UA_NODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE),
-                                      UA_QUALIFIEDNAME(0, "BaseObjectType"), baseobj_attr, NULL, NULL);
+    addNode_begin(server, UA_NODECLASS_OBJECTTYPE, UA_NS0ID_BASEOBJECTTYPE, "BaseObjectType",
+                  &baseobj_attr, &UA_TYPES[UA_TYPES_OBJECTTYPEATTRIBUTES], false);
 
     addObjectTypeNode(server, "ModellingRuleType", UA_NS0ID_MODELLINGRULETYPE,
                       false, UA_NS0ID_BASEOBJECTTYPE);
@@ -458,12 +478,7 @@ void UA_Server_createNS0(UA_Server *server) {
     /* Root and below */
     /******************/
 
-    UA_ObjectAttributes root_attr = UA_ObjectAttributes_default;
-    root_attr.displayName = UA_LOCALIZEDTEXT("", "Root");
-    UA_Server_addObjectNode_begin(server, UA_NODEID_NUMERIC(0, UA_NS0ID_ROOTFOLDER),
-                                  UA_QUALIFIEDNAME(0, "Root"), root_attr, NULL, NULL);
-    addReferenceInternal(server, UA_NS0ID_ROOTFOLDER, UA_NS0ID_HASTYPEDEFINITION,
-                         UA_NS0ID_FOLDERTYPE, true);
+    addObjectNode(server, "Root", UA_NS0ID_ROOTFOLDER, 0, 0, UA_NS0ID_FOLDERTYPE);
 
     addObjectNode(server, "Objects", UA_NS0ID_OBJECTSFOLDER, UA_NS0ID_ROOTFOLDER,
                   UA_NS0ID_ORGANIZES, UA_NS0ID_FOLDERTYPE);
@@ -473,23 +488,23 @@ void UA_Server_createNS0(UA_Server *server) {
 
     addObjectNode(server, "ReferenceTypes", UA_NS0ID_REFERENCETYPESFOLDER, UA_NS0ID_TYPESFOLDER,
                   UA_NS0ID_ORGANIZES, UA_NS0ID_FOLDERTYPE);
-    addReferenceInternal(server, UA_NS0ID_REFERENCETYPESFOLDER, UA_NS0ID_ORGANIZES,
-                         UA_NS0ID_REFERENCES, true);
+    addNode_finish(server, UA_NS0ID_REFERENCES, UA_NS0ID_REFERENCETYPESFOLDER,
+                   UA_NS0ID_ORGANIZES, 0, true);
 
     addObjectNode(server, "DataTypes", UA_NS0ID_DATATYPESFOLDER, UA_NS0ID_TYPESFOLDER,
                   UA_NS0ID_ORGANIZES, UA_NS0ID_FOLDERTYPE);
-    addReferenceInternal(server, UA_NS0ID_DATATYPESFOLDER, UA_NS0ID_ORGANIZES,
-                         UA_NS0ID_BASEDATATYPE, true);
+    addNode_finish(server, UA_NS0ID_BASEDATATYPE, UA_NS0ID_DATATYPESFOLDER,
+                   UA_NS0ID_ORGANIZES, 0, true);
 
     addObjectNode(server, "VariableTypes", UA_NS0ID_VARIABLETYPESFOLDER, UA_NS0ID_TYPESFOLDER,
                   UA_NS0ID_ORGANIZES, UA_NS0ID_FOLDERTYPE);
-    addReferenceInternal(server, UA_NS0ID_VARIABLETYPESFOLDER, UA_NS0ID_ORGANIZES,
-                         UA_NS0ID_BASEVARIABLETYPE, true);
+    addNode_finish(server, UA_NS0ID_BASEVARIABLETYPE, UA_NS0ID_VARIABLETYPESFOLDER,
+                   UA_NS0ID_ORGANIZES, 0, true);
 
     addObjectNode(server, "ObjectTypes", UA_NS0ID_OBJECTTYPESFOLDER, UA_NS0ID_TYPESFOLDER,
                   UA_NS0ID_ORGANIZES, UA_NS0ID_FOLDERTYPE);
-    addReferenceInternal(server, UA_NS0ID_OBJECTTYPESFOLDER, UA_NS0ID_ORGANIZES,
-                         UA_NS0ID_BASEOBJECTTYPE, true);
+    addNode_finish(server, UA_NS0ID_BASEOBJECTTYPE, UA_NS0ID_OBJECTTYPESFOLDER,
+                   UA_NS0ID_ORGANIZES, 0, true);
 
     addObjectNode(server, "EventTypes", UA_NS0ID_EVENTTYPESFOLDER, UA_NS0ID_TYPESFOLDER,
                   UA_NS0ID_ORGANIZES, UA_NS0ID_FOLDERTYPE);
@@ -517,8 +532,8 @@ void UA_Server_createNS0(UA_Server *server) {
     /* Begin Server object */ 
     UA_ObjectAttributes server_attr = UA_ObjectAttributes_default;
     server_attr.displayName = UA_LOCALIZEDTEXT("", "Server");
-    UA_Server_addObjectNode_begin(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER),
-                                  UA_QUALIFIEDNAME(0, "Server"), server_attr, NULL, NULL);
+    addNode_begin(server, UA_NODECLASS_OBJECT, UA_NS0ID_SERVER, "Server",
+                  &server_attr, &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES], false);
 
     /* ServerArray */
     UA_Variant_setArray(&var, &server->config.applicationDescription.applicationUri,
@@ -534,21 +549,19 @@ void UA_Server_createNS0(UA_Server *server) {
     nsarray_attr.minimumSamplingInterval = 50.0;
     nsarray_attr.dataType = UA_TYPES[UA_TYPES_STRING].typeId;
     nsarray_attr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;
-    UA_Server_addVariableNode_begin(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_NAMESPACEARRAY),
-                                    UA_QUALIFIEDNAME(0, "NamespaceArray"), nsarray_attr, NULL, NULL);
+    addNode_begin(server, UA_NODECLASS_VARIABLE, UA_NS0ID_SERVER_NAMESPACEARRAY,
+                  "NamespaceArray", &nsarray_attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES], false);
     UA_DataSource nsarray_datasource =  {readNamespaces, writeNamespaces};
     UA_Server_setVariableNode_dataSource(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_NAMESPACEARRAY),
                                          nsarray_datasource);
-    UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_NAMESPACEARRAY),
-                             UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER),
-                             UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY),
-                             UA_NODEID_NUMERIC(0, UA_NS0ID_PROPERTYTYPE));
+    addNode_finish(server, UA_NS0ID_SERVER_NAMESPACEARRAY,
+                   UA_NS0ID_SERVER, UA_NS0ID_HASPROPERTY, UA_NS0ID_PROPERTYTYPE, false);
 
     /* Begin ServerCapabilities */
     UA_ObjectAttributes servercap_attr = UA_ObjectAttributes_default;
     servercap_attr.displayName = UA_LOCALIZEDTEXT("", "ServerCapabilities");
-    UA_Server_addObjectNode_begin(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES),
-                                  UA_QUALIFIEDNAME(0, "ServerCapabilities"), servercap_attr, NULL, NULL);
+    addNode_begin(server, UA_NODECLASS_OBJECT, UA_NS0ID_SERVER_SERVERCAPABILITIES,
+                  "ServerCapabilities", &servercap_attr, &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES], false);
     
     UA_String enLocale = UA_STRING("en");
     UA_Variant_setArray(&var, &enLocale, 1, &UA_TYPES[UA_TYPES_STRING]);
@@ -614,16 +627,14 @@ void UA_Server_createNS0(UA_Server *server) {
                   UA_NS0ID_SERVER_SERVERCAPABILITIES, UA_NS0ID_HASPROPERTY, UA_NS0ID_FOLDERTYPE);
 
     /* Finish ServerCapabilities */
-    UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES),
-                             UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER),
-                             UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
-                             UA_NODEID_NUMERIC(0, UA_NS0ID_SERVERCAPABILITIESTYPE));
+    addNode_finish(server, UA_NS0ID_SERVER_SERVERCAPABILITIES, UA_NS0ID_SERVER,
+                   UA_NS0ID_HASCOMPONENT, UA_NS0ID_SERVERCAPABILITIESTYPE, false);
 
     /* Begin ServerDiagnostics */
     UA_ObjectAttributes serverdiag_attr = UA_ObjectAttributes_default;
     serverdiag_attr.displayName = UA_LOCALIZEDTEXT("", "ServerDiagnostics");
-    UA_Server_addObjectNode_begin(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERDIAGNOSTICS),
-                                  UA_QUALIFIEDNAME(0, "ServerDiagnostics"), serverdiag_attr, NULL, NULL);
+    addNode_begin(server, UA_NODECLASS_OBJECT, UA_NS0ID_SERVER_SERVERDIAGNOSTICS, "ServerDiagnostics",
+                  &serverdiag_attr, &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES], false);
     
     UA_Boolean enabledFlag = false;
     UA_Variant_setScalar(&var, &enabledFlag, &UA_TYPES[UA_TYPES_BOOLEAN]);
@@ -632,10 +643,8 @@ void UA_Server_createNS0(UA_Server *server) {
                     UA_NS0ID_HASPROPERTY, UA_NS0ID_PROPERTYTYPE);
 
     /* Finish ServerDiagnostics */
-    UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERDIAGNOSTICS),
-                             UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER),
-                             UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
-                             UA_NODEID_NUMERIC(0, UA_NS0ID_SERVERDIAGNOSTICSTYPE));
+    addNode_finish(server, UA_NS0ID_SERVER_SERVERDIAGNOSTICS, UA_NS0ID_SERVER,
+                   UA_NS0ID_HASCOMPONENT, UA_NS0ID_SERVERDIAGNOSTICSTYPE, false);
 
     UA_DataSource statusDS = {readStatus, NULL};
     addDataSourceVariableNode(server, UA_NS0ID_SERVER_SERVERSTATUS, "ServerStatus", -1,
@@ -742,9 +751,8 @@ void UA_Server_createNS0(UA_Server *server) {
     addmethodattributes.displayName = UA_LOCALIZEDTEXT("", "GetMonitoredItems");
     addmethodattributes.executable = true;
     addmethodattributes.userExecutable = true;
-    UA_Server_addMethodNode_begin(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_GETMONITOREDITEMS),
-                                  UA_QUALIFIEDNAME(0, "GetMonitoredItems"),
-                                  addmethodattributes, NULL, NULL);
+    addNode_begin(server, UA_NODECLASS_METHOD, UA_NS0ID_SERVER_GETMONITOREDITEMS,
+                  "GetMonitoredItems", &addmethodattributes, &UA_TYPES[UA_TYPES_METHODATTRIBUTES], false);
 
     /* Add the arguments manually to get the nodeids right */
     UA_Argument inputArguments;
@@ -782,10 +790,8 @@ void UA_Server_createNS0(UA_Server *server) {
 #endif /* defined(UA_ENABLE_METHODCALLS) && defined(UA_ENABLE_SUBSCRIPTIONS) */
 
     /* Finish adding the server object */
-    UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER),
-                             UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
-                             UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
-                             UA_NODEID_NUMERIC(0, UA_NS0ID_SERVERTYPE));
+    addNode_finish(server, UA_NS0ID_SERVER, UA_NS0ID_OBJECTSFOLDER,
+                   UA_NS0ID_ORGANIZES, UA_NS0ID_SERVERTYPE, false);
 }
 
 #endif /* UA_ENABLE_GENERATE_NAMESPACE0 */

+ 51 - 52
src/server/ua_server_utils.c

@@ -86,12 +86,12 @@ UA_NumericRange_parseFromString(UA_NumericRange *range, const UA_String *str) {
 /********************************/
 
 UA_Boolean
-isNodeInTree(UA_NodeStore *ns, const UA_NodeId *leafNode, const UA_NodeId *nodeToFind,
+isNodeInTree(UA_Nodestore *ns, const UA_NodeId *leafNode, const UA_NodeId *nodeToFind,
              const UA_NodeId *referenceTypeIds, size_t referenceTypeIdsSize) {
     if(UA_NodeId_equal(nodeToFind, leafNode))
         return true;
 
-    const UA_Node *node = UA_NodeStore_get(ns, leafNode);
+    const UA_Node *node = ns->getNode(ns->context, leafNode);
     if(!node)
         return false;
 
@@ -115,24 +115,33 @@ isNodeInTree(UA_NodeStore *ns, const UA_NodeId *leafNode, const UA_NodeId *nodeT
         /* Match the targets or recurse */
         for(size_t j = 0; j < refs->targetIdsSize; ++j) {
             if(isNodeInTree(ns, &refs->targetIds[j].nodeId, nodeToFind,
-                            referenceTypeIds, referenceTypeIdsSize))
+                            referenceTypeIds, referenceTypeIdsSize)) {
+                ns->releaseNode(ns->context, node);
                 return true;
+            }
         }
     }
+
+    ns->releaseNode(ns->context, node);
     return false;
 }
 
-const UA_NodeId *
+const UA_Node *
 getNodeType(UA_Server *server, const UA_Node *node) {
+    /* The reference to the parent is different for variable and variabletype */
     UA_NodeId parentRef;
     UA_Boolean inverse;
-
-    /* The reference to the parent is different for variable and variabletype */
+    UA_NodeClass typeNodeClass;
     switch(node->nodeClass) {
     case UA_NODECLASS_OBJECT:
+        parentRef = UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION);
+        inverse = false;
+        typeNodeClass = UA_NODECLASS_OBJECTTYPE;
+        break;
     case UA_NODECLASS_VARIABLE:
         parentRef = UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION);
         inverse = false;
+        typeNodeClass = UA_NODECLASS_VARIABLETYPE;
         break;
     case UA_NODECLASS_OBJECTTYPE:
     case UA_NODECLASS_VARIABLETYPE:
@@ -140,45 +149,29 @@ getNodeType(UA_Server *server, const UA_Node *node) {
     case UA_NODECLASS_DATATYPE:
         parentRef = UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE);
         inverse = true;
+        typeNodeClass = node->nodeClass;
         break;
     default:
-        return &UA_NODEID_NULL;
+        return NULL;
     }
 
-    /* Stop at the first matching candidate */
+    /* Return the first matching candidate */
     for(size_t i = 0; i < node->referencesSize; ++i) {
         if(node->references[i].isInverse != inverse)
             continue;
         if(!UA_NodeId_equal(&node->references[i].referenceTypeId, &parentRef))
             continue;
         UA_assert(node->references[i].targetIdsSize > 0);
-        return &node->references[i].targetIds[0].nodeId;
+        const UA_NodeId *targetId = &node->references[i].targetIds[0].nodeId;
+        const UA_Node *type = UA_Nodestore_get(server, targetId);
+        if(!type)
+            continue;
+        if(type->nodeClass == typeNodeClass)
+            return type;
+        UA_Nodestore_release(server, type);
     }
-    return &UA_NODEID_NULL;
-}
 
-const UA_VariableTypeNode *
-getVariableNodeType(UA_Server *server, const UA_VariableNode *node) {
-    const UA_NodeId *vtId = getNodeType(server, (const UA_Node*)node);
-    const UA_Node *vt = UA_NodeStore_get(server->nodestore, vtId);
-    if(!vt || vt->nodeClass != UA_NODECLASS_VARIABLETYPE) {
-        UA_LOG_DEBUG(server->config.logger, UA_LOGCATEGORY_SERVER,
-                     "No VariableType for the node found");
-        return NULL;
-    }
-    return (const UA_VariableTypeNode*)vt;
-}
-
-const UA_ObjectTypeNode *
-getObjectNodeType(UA_Server *server, const UA_ObjectNode *node) {
-    const UA_NodeId *otId = getNodeType(server, (const UA_Node*)node);
-    const UA_Node *ot = UA_NodeStore_get(server->nodestore, otId);
-    if(!ot || ot->nodeClass != UA_NODECLASS_OBJECTTYPE) {
-        UA_LOG_DEBUG(server->config.logger, UA_LOGCATEGORY_SERVER,
-                     "No ObjectType for the node found");
-        return NULL;
-    }
-    return (const UA_ObjectTypeNode*)ot;
+    return NULL;
 }
 
 UA_Boolean
@@ -251,7 +244,7 @@ getTypeHierarchyFromNode(UA_NodeId **results_ptr, size_t *results_count,
 }
 
 UA_StatusCode
-getTypeHierarchy(UA_NodeStore *ns, const UA_NodeId *leafType,
+getTypeHierarchy(UA_Nodestore *ns, const UA_NodeId *leafType,
                  UA_NodeId **typeHierarchy, size_t *typeHierarchySize) {
     /* Allocate the results array. Probably too big, but saves mallocs. */
     size_t results_size = 20;
@@ -270,7 +263,7 @@ getTypeHierarchy(UA_NodeStore *ns, const UA_NodeId *leafType,
     /* Loop over the array members .. and add new elements to the end */
     for(size_t idx = 0; idx < results_count; ++idx) {
         /* Get the node */
-        const UA_Node *node = UA_NodeStore_get(ns, &results[idx]);
+        const UA_Node *node = ns->getNode(ns->context, &results[idx]);
 
         /* Invalid node, remove from the array */
         if(!node) {
@@ -283,8 +276,14 @@ getTypeHierarchy(UA_NodeStore *ns, const UA_NodeId *leafType,
         /* Add references from the current node to the end of the array */
         retval = getTypeHierarchyFromNode(&results, &results_count,
                                           &results_size, node);
-        if(retval != UA_STATUSCODE_GOOD)
+
+        /* Release the node */
+        ns->releaseNode(ns->context, node);
+
+        if(retval != UA_STATUSCODE_GOOD) {
+            UA_Array_delete(results, results_count, &UA_TYPES[UA_TYPES_NODEID]);
             return retval;
+        }
     }
 
     /* Zero results. The leaf node was not found */
@@ -299,36 +298,35 @@ getTypeHierarchy(UA_NodeStore *ns, const UA_NodeId *leafType,
 }
 
 /* For mulithreading: make a copy of the node, edit and replace.
- * For singletrheading: edit the original */
+ * For singlethreading: edit the original */
 UA_StatusCode
 UA_Server_editNode(UA_Server *server, UA_Session *session,
                    const UA_NodeId *nodeId, UA_EditNodeCallback callback,
                    const void *data) {
 #ifndef UA_ENABLE_MULTITHREADING
-    const UA_Node *node = UA_NodeStore_get(server->nodestore, nodeId);
+    const UA_Node *node = UA_Nodestore_get(server, nodeId);
     if(!node)
         return UA_STATUSCODE_BADNODEIDUNKNOWN;
-    UA_Node *editNode = (UA_Node*)(uintptr_t)node; // dirty cast
-    return callback(server, session, editNode, data);
+    UA_StatusCode retval = callback(server, session,
+                                    (UA_Node*)(uintptr_t)node, data);
+    UA_Nodestore_release(server, node);
+    return retval;
 #else
     UA_StatusCode retval;
     do {
-        UA_RCU_LOCK();
-        UA_Node *copy = UA_NodeStore_getCopy(server->nodestore, nodeId);
-        if(!copy) {
-            UA_RCU_UNLOCK();
-            return UA_STATUSCODE_BADOUTOFMEMORY;
-        }
-        retval = callback(server, session, copy, data);
+        UA_Node *node;
+        retval = server->config.nodestore.getNodeCopy(server->config.nodestore.context,
+                                                      nodeId, &node);
+        if(retval != UA_STATUSCODE_GOOD)
+            return retval;
+        retval = callback(server, session, node, data);
         if(retval != UA_STATUSCODE_GOOD) {
-            UA_NodeStore_deleteNode(copy);
-            UA_RCU_UNLOCK();
+            server->config.nodestore.deleteNode(server->config.nodestore.context, node);
             return retval;
         }
-        retval = UA_NodeStore_replace(server->nodestore, copy);
-        UA_RCU_UNLOCK();
+        retval = server->config.nodestore.replaceNode(server->config.nodestore.context, node);
     } while(retval != UA_STATUSCODE_GOOD);
-    return UA_STATUSCODE_GOOD;
+    return retval;
 #endif
 }
 
@@ -445,3 +443,4 @@ const UA_ViewAttributes UA_ViewAttributes_default = {
     false,                  /* containsNoLoops */
     0                       /* eventNotifier */
 };
+

+ 0 - 9
src/server/ua_server_worker.c

@@ -55,7 +55,6 @@ workerLoop(UA_Worker *worker) {
     /* Initialize the (thread local) random seed with the ram address
      * of the worker. Not for security-critical entropy! */
     UA_random_seed((uintptr_t)worker);
-    rcu_register_thread();
 
     while(*running) {
         UA_atomic_add(counter, 1);
@@ -76,15 +75,10 @@ workerLoop(UA_Worker *worker) {
             continue;
         }
         
-        UA_RCU_LOCK();
         dc->callback(server, dc->data);
         UA_free(dc);
-        UA_RCU_UNLOCK();
     }
 
-    UA_ASSERT_RCU_UNLOCKED();
-    rcu_barrier();
-    rcu_unregister_thread();
     UA_LOG_DEBUG(server->config.logger, UA_LOGCATEGORY_SERVER,
                  "Worker shut down");
     return NULL;
@@ -389,9 +383,6 @@ UA_Server_run_shutdown(UA_Server *server) {
      * This also executes the delayed callbacks. */
     emptyDispatchQueue(server);
     
-    /* Wait for all scheduled call_rcu work to complete */
-    UA_ASSERT_RCU_UNLOCKED();
-    rcu_barrier();
 #endif
 
     /* Stop multicast discovery */

+ 172 - 204
src/server/ua_services_attribute.c

@@ -5,15 +5,9 @@
 #include "ua_server_internal.h"
 #include "ua_services.h"
 
-/* Force cast from const data for zero-copy reading. The storage type is set to
- * nodelete. So the value is not deleted. Use with care! */
-static void
-forceVariantSetScalar(UA_Variant *v, const void *p, const UA_DataType *t) {
-    UA_Variant_init(v);
-    v->type = t;
-    v->data = (void*)(uintptr_t)p;
-    v->storageType = UA_VARIANT_DATA_NODELETE;
-}
+/******************/
+/* Access Control */
+/******************/
 
 static UA_UInt32
 getUserWriteMask(UA_Server *server, const UA_Session *session,
@@ -77,8 +71,7 @@ readIsAbstractAttribute(const UA_Node *node, UA_Variant *v) {
     default:
         return UA_STATUSCODE_BADATTRIBUTEIDINVALID;
     }
-    forceVariantSetScalar(v, isAbstract, &UA_TYPES[UA_TYPES_BOOLEAN]);
-    return UA_STATUSCODE_GOOD;
+    return UA_Variant_setScalarCopy(v, isAbstract, &UA_TYPES[UA_TYPES_BOOLEAN]);
 }
 
 static UA_StatusCode
@@ -86,15 +79,13 @@ readValueAttributeFromNode(UA_Server *server, UA_Session *session,
                            const UA_VariableNode *vn, UA_DataValue *v,
                            UA_NumericRange *rangeptr) {
     if(vn->value.data.callback.onRead) {
-        UA_RCU_UNLOCK();
         vn->value.data.callback.onRead(server, &session->sessionId,
                                        session->sessionHandle, &vn->nodeId,
                                        vn->context, rangeptr, &vn->value.data.value);
-        UA_RCU_LOCK();
-#ifdef UA_ENABLE_MULTITHREADING
-        /* Reopen the node to see the changes (multithreading only) */
-        vn = (const UA_VariableNode*)UA_NodeStore_get(server->nodestore, &vn->nodeId);
-#endif
+        const UA_Node *old = (const UA_Node *)vn;
+        /* Reopen the node to see the changes from onRead */
+        vn = (const UA_VariableNode*)UA_Nodestore_get(server, &vn->nodeId);
+        UA_Nodestore_release(server, old);
     }
     if(rangeptr)
         return UA_Variant_copyRange(&vn->value.data.value.value, &v->value, *rangeptr);
@@ -112,12 +103,8 @@ readValueAttributeFromDataSource(UA_Server *server, UA_Session *session,
         return UA_STATUSCODE_BADINTERNALERROR;
     UA_Boolean sourceTimeStamp = (timestamps == UA_TIMESTAMPSTORETURN_SOURCE ||
                                   timestamps == UA_TIMESTAMPSTORETURN_BOTH);
-    UA_RCU_UNLOCK();
-    UA_StatusCode retval =
-        vn->value.dataSource.read(server, &session->sessionId, session->sessionHandle,
-                                  &vn->nodeId, vn->context, sourceTimeStamp, rangeptr, v);
-    UA_RCU_LOCK();
-    return retval;
+    return vn->value.dataSource.read(server, &session->sessionId, session->sessionHandle,
+                                     &vn->nodeId, vn->context, sourceTimeStamp, rangeptr, v);
 }
 
 static UA_StatusCode
@@ -150,8 +137,7 @@ readValueAttributeComplete(UA_Server *server, UA_Session *session,
 UA_StatusCode
 readValueAttribute(UA_Server *server, UA_Session *session,
                    const UA_VariableNode *vn, UA_DataValue *v) {
-    return readValueAttributeComplete(server, session, vn,
-                                      UA_TIMESTAMPSTORETURN_NEITHER, NULL, v);
+    return readValueAttributeComplete(server, session, vn, UA_TIMESTAMPSTORETURN_NEITHER, NULL, v);
 }
 
 static const UA_String binEncoding = {sizeof("Default Binary")-1, (UA_Byte*)"Default Binary"};
@@ -188,7 +174,7 @@ Operation_Read(UA_Server *server, UA_Session *session,
     }
 
     /* Get the node */
-    const UA_Node *node = UA_NodeStore_get(server->nodestore, &id->nodeId);
+    const UA_Node *node = UA_Nodestore_get(server, &id->nodeId);
     if(!node) {
         v->hasStatus = true;
         v->status = UA_STATUSCODE_BADNODEIDUNKNOWN;
@@ -199,22 +185,22 @@ Operation_Read(UA_Server *server, UA_Session *session,
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
     switch(id->attributeId) {
     case UA_ATTRIBUTEID_NODEID:
-        forceVariantSetScalar(&v->value, &node->nodeId, &UA_TYPES[UA_TYPES_NODEID]);
+        retval = UA_Variant_setScalarCopy(&v->value, &node->nodeId, &UA_TYPES[UA_TYPES_NODEID]);
         break;
     case UA_ATTRIBUTEID_NODECLASS:
-        forceVariantSetScalar(&v->value, &node->nodeClass, &UA_TYPES[UA_TYPES_NODECLASS]);
+        retval = UA_Variant_setScalarCopy(&v->value, &node->nodeClass, &UA_TYPES[UA_TYPES_NODECLASS]);
         break;
     case UA_ATTRIBUTEID_BROWSENAME:
-        forceVariantSetScalar(&v->value, &node->browseName, &UA_TYPES[UA_TYPES_QUALIFIEDNAME]);
+        retval = UA_Variant_setScalarCopy(&v->value, &node->browseName, &UA_TYPES[UA_TYPES_QUALIFIEDNAME]);
         break;
     case UA_ATTRIBUTEID_DISPLAYNAME:
-        forceVariantSetScalar(&v->value, &node->displayName, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]);
+        retval = UA_Variant_setScalarCopy(&v->value, &node->displayName, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]);
         break;
     case UA_ATTRIBUTEID_DESCRIPTION:
-        forceVariantSetScalar(&v->value, &node->description, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]);
+        retval = UA_Variant_setScalarCopy(&v->value, &node->description, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]);
         break;
     case UA_ATTRIBUTEID_WRITEMASK:
-        forceVariantSetScalar(&v->value, &node->writeMask, &UA_TYPES[UA_TYPES_UINT32]);
+        retval = UA_Variant_setScalarCopy(&v->value, &node->writeMask, &UA_TYPES[UA_TYPES_UINT32]);
         break;
     case UA_ATTRIBUTEID_USERWRITEMASK: {
         UA_UInt32 userWriteMask = getUserWriteMask(server, session, node);
@@ -225,23 +211,23 @@ Operation_Read(UA_Server *server, UA_Session *session,
         break;
     case UA_ATTRIBUTEID_SYMMETRIC:
         CHECK_NODECLASS(UA_NODECLASS_REFERENCETYPE);
-        forceVariantSetScalar(&v->value, &((const UA_ReferenceTypeNode*)node)->symmetric,
-                              &UA_TYPES[UA_TYPES_BOOLEAN]);
+        retval = UA_Variant_setScalarCopy(&v->value, &((const UA_ReferenceTypeNode*)node)->symmetric,
+                                          &UA_TYPES[UA_TYPES_BOOLEAN]);
         break;
     case UA_ATTRIBUTEID_INVERSENAME:
         CHECK_NODECLASS(UA_NODECLASS_REFERENCETYPE);
-        forceVariantSetScalar(&v->value, &((const UA_ReferenceTypeNode*)node)->inverseName,
-                              &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]);
+        retval = UA_Variant_setScalarCopy(&v->value, &((const UA_ReferenceTypeNode*)node)->inverseName,
+                                          &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]);
         break;
     case UA_ATTRIBUTEID_CONTAINSNOLOOPS:
         CHECK_NODECLASS(UA_NODECLASS_VIEW);
-        forceVariantSetScalar(&v->value, &((const UA_ViewNode*)node)->containsNoLoops,
-                              &UA_TYPES[UA_TYPES_BOOLEAN]);
+        retval = UA_Variant_setScalarCopy(&v->value, &((const UA_ViewNode*)node)->containsNoLoops,
+                                          &UA_TYPES[UA_TYPES_BOOLEAN]);
         break;
     case UA_ATTRIBUTEID_EVENTNOTIFIER:
         CHECK_NODECLASS(UA_NODECLASS_VIEW | UA_NODECLASS_OBJECT);
-        forceVariantSetScalar(&v->value, &((const UA_ViewNode*)node)->eventNotifier,
-                              &UA_TYPES[UA_TYPES_BYTE]);
+        retval = UA_Variant_setScalarCopy(&v->value, &((const UA_ViewNode*)node)->eventNotifier,
+                                          &UA_TYPES[UA_TYPES_BYTE]);
         break;
     case UA_ATTRIBUTEID_VALUE: {
         CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
@@ -257,13 +243,13 @@ Operation_Read(UA_Server *server, UA_Session *session,
     }
     case UA_ATTRIBUTEID_DATATYPE:
         CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
-        forceVariantSetScalar(&v->value, &((const UA_VariableTypeNode*)node)->dataType,
-                              &UA_TYPES[UA_TYPES_NODEID]);
+        retval = UA_Variant_setScalarCopy(&v->value, &((const UA_VariableTypeNode*)node)->dataType,
+                                          &UA_TYPES[UA_TYPES_NODEID]);
         break;
     case UA_ATTRIBUTEID_VALUERANK:
         CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
-        forceVariantSetScalar(&v->value, &((const UA_VariableTypeNode*)node)->valueRank,
-                              &UA_TYPES[UA_TYPES_INT32]);
+        retval = UA_Variant_setScalarCopy(&v->value, &((const UA_VariableTypeNode*)node)->valueRank,
+                                          &UA_TYPES[UA_TYPES_INT32]);
         break;
     case UA_ATTRIBUTEID_ARRAYDIMENSIONS:
         CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
@@ -271,40 +257,43 @@ Operation_Read(UA_Server *server, UA_Session *session,
         break;
     case UA_ATTRIBUTEID_ACCESSLEVEL:
         CHECK_NODECLASS(UA_NODECLASS_VARIABLE);
-        forceVariantSetScalar(&v->value, &((const UA_VariableNode*)node)->accessLevel,
-                              &UA_TYPES[UA_TYPES_BYTE]);
+        retval = UA_Variant_setScalarCopy(&v->value, &((const UA_VariableNode*)node)->accessLevel,
+                                          &UA_TYPES[UA_TYPES_BYTE]);
         break;
     case UA_ATTRIBUTEID_USERACCESSLEVEL: {
         CHECK_NODECLASS(UA_NODECLASS_VARIABLE);
         UA_Byte userAccessLevel = getUserAccessLevel(server, session,
                                                      (const UA_VariableNode*)node);
-        UA_Variant_setScalarCopy(&v->value, &userAccessLevel, &UA_TYPES[UA_TYPES_BYTE]);
+        retval = UA_Variant_setScalarCopy(&v->value, &userAccessLevel, &UA_TYPES[UA_TYPES_BYTE]);
         break; }
     case UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL:
         CHECK_NODECLASS(UA_NODECLASS_VARIABLE);
-        forceVariantSetScalar(&v->value, &((const UA_VariableNode*)node)->minimumSamplingInterval,
-                              &UA_TYPES[UA_TYPES_DOUBLE]);
+        retval = UA_Variant_setScalarCopy(&v->value, &((const UA_VariableNode*)node)->minimumSamplingInterval,
+                                          &UA_TYPES[UA_TYPES_DOUBLE]);
         break;
     case UA_ATTRIBUTEID_HISTORIZING:
         CHECK_NODECLASS(UA_NODECLASS_VARIABLE);
-        forceVariantSetScalar(&v->value, &((const UA_VariableNode*)node)->historizing,
-                              &UA_TYPES[UA_TYPES_BOOLEAN]);
+        retval = UA_Variant_setScalarCopy(&v->value, &((const UA_VariableNode*)node)->historizing,
+                                          &UA_TYPES[UA_TYPES_BOOLEAN]);
         break;
     case UA_ATTRIBUTEID_EXECUTABLE:
         CHECK_NODECLASS(UA_NODECLASS_METHOD);
-        forceVariantSetScalar(&v->value, &((const UA_MethodNode*)node)->executable,
-                              &UA_TYPES[UA_TYPES_BOOLEAN]);
+        retval = UA_Variant_setScalarCopy(&v->value, &((const UA_MethodNode*)node)->executable,
+                                          &UA_TYPES[UA_TYPES_BOOLEAN]);
         break;
     case UA_ATTRIBUTEID_USEREXECUTABLE: {
         CHECK_NODECLASS(UA_NODECLASS_METHOD);
         UA_Boolean userExecutable = getUserExecutable(server, session,
                                                       (const UA_MethodNode*)node);
-        UA_Variant_setScalarCopy(&v->value, &userExecutable, &UA_TYPES[UA_TYPES_BOOLEAN]);
+        retval = UA_Variant_setScalarCopy(&v->value, &userExecutable, &UA_TYPES[UA_TYPES_BOOLEAN]);
         break; }
     default:
         retval = UA_STATUSCODE_BADATTRIBUTEIDINVALID;
     }
 
+    /* Release nodes */
+    UA_Nodestore_release(server, node);
+
     /* Return error code when reading has failed */
     if(retval != UA_STATUSCODE_GOOD) {
         v->hasStatus = true;
@@ -366,9 +355,7 @@ UA_Server_readWithSession(UA_Server *server, UA_Session *session,
     UA_DataValue dv;
     UA_DataValue_init(&dv);
     op_timestampsToReturn = timestamps;
-    UA_RCU_LOCK();
     Operation_Read(server, session, item, &dv);
-    UA_RCU_UNLOCK();
     return dv;
 }
 
@@ -429,7 +416,7 @@ __UA_Server_read(UA_Server *server, const UA_NodeId *nodeId,
 }
 
 /*****************/
-/* Write Service */
+/* Type Checking */
 /*****************/
 
 enum type_equivalence {
@@ -467,8 +454,8 @@ compatibleDataType(UA_Server *server, const UA_NodeId *dataType,
     if(UA_NodeId_equal(constraintDataType, &UA_TYPES[UA_TYPES_VARIANT].typeId))
         return true;
 
-    /* Is the value-type the required datatype or a subtype? */
-    if(isNodeInTree(server->nodestore, dataType, constraintDataType, &subtypeId, 1))
+    /* Is the value-type a subtype of the required type? */
+    if(isNodeInTree(&server->config.nodestore, dataType, constraintDataType, &subtypeId, 1))
         return true;
 
     /* If value is a built-in type: The target data type may be a sub type of
@@ -478,13 +465,13 @@ compatibleDataType(UA_Server *server, const UA_NodeId *dataType,
     if(dataType->namespaceIndex == 0 &&
        dataType->identifierType == UA_NODEIDTYPE_NUMERIC &&
        dataType->identifier.numeric <= 25 &&
-       isNodeInTree(server->nodestore, constraintDataType,
+       isNodeInTree(&server->config.nodestore, constraintDataType,
                     dataType, &subtypeId, 1))
         return true;
 
     /* Enum allows Int32 (only) */
     if(UA_NodeId_equal(dataType, &UA_TYPES[UA_TYPES_INT32].typeId) &&
-       isNodeInTree(server->nodestore, constraintDataType, &enumNodeId, &subtypeId, 1))
+       isNodeInTree(&server->config.nodestore, constraintDataType, &enumNodeId, &subtypeId, 1))
         return true;
 
     return false;
@@ -581,56 +568,11 @@ compatibleArrayDimensions(size_t constraintArrayDimensionsSize,
     return UA_STATUSCODE_GOOD;
 }
 
-/* Returns the pointer to a datavalue with a possibly transformed type to match
-   the description */
-static const UA_Variant *
-convertToMatchingValue(UA_Server *server, const UA_Variant *value,
-                       const UA_NodeId *targetDataTypeId,
-                       UA_Variant *editableValue) {
-    const UA_DataType *targetDataType = UA_findDataType(targetDataTypeId);
-    if(!targetDataType)
-        return NULL;
-
-    /* A string is written to a byte array. the valuerank and array dimensions
-     * are checked later */
-    if(targetDataType == &UA_TYPES[UA_TYPES_BYTE] &&
-       value->type == &UA_TYPES[UA_TYPES_BYTESTRING] &&
-       UA_Variant_isScalar(value)) {
-        UA_ByteString *str = (UA_ByteString*)value->data;
-        editableValue->storageType = UA_VARIANT_DATA_NODELETE;
-        editableValue->type = &UA_TYPES[UA_TYPES_BYTE];
-        editableValue->arrayLength = str->length;
-        editableValue->data = str->data;
-        return editableValue;
-    }
-
-    /* An enum was sent as an int32, or an opaque type as a bytestring. This
-     * is detected with the typeIndex indicating the "true" datatype. */
-    enum type_equivalence te1 = typeEquivalence(targetDataType);
-    enum type_equivalence te2 = typeEquivalence(value->type);
-    if(te1 != TYPE_EQUIVALENCE_NONE && te1 == te2) {
-        *editableValue = *value;
-        editableValue->storageType = UA_VARIANT_DATA_NODELETE;
-        editableValue->type = targetDataType;
-        return editableValue;
-    }
-
-    /* No more possible equivalencies */
-    return NULL;
-}
-
-/* Test whether the value matches a variable definition given by
- * - datatype
- * - valueranke
- * - array dimensions.
- * Sometimes it can be necessary to transform the content of the value, e.g.
- * byte array to bytestring or uint32 to some enum. If editableValue is non-NULL,
- * we try to create a matching variant that points to the original data. */
 UA_StatusCode
-typeCheckValue(UA_Server *server, const UA_NodeId *targetDataTypeId,
-               UA_Int32 targetValueRank, size_t targetArrayDimensionsSize,
-               const UA_UInt32 *targetArrayDimensions, const UA_Variant *value,
-               const UA_NumericRange *range, UA_Variant *editableValue) {
+compatibleValue(UA_Server *server, const UA_NodeId *targetDataTypeId,
+                UA_Int32 targetValueRank, size_t targetArrayDimensionsSize,
+                const UA_UInt32 *targetArrayDimensions, const UA_Variant *value,
+                const UA_NumericRange *range) {
     /* Empty variant is only allowed for BaseDataType */
     if(!value->type) {
         if(UA_NodeId_equal(targetDataTypeId, &UA_TYPES[UA_TYPES_VARIANT].typeId) ||
@@ -644,14 +586,8 @@ typeCheckValue(UA_Server *server, const UA_NodeId *targetDataTypeId,
 
     /* Has the value a subtype of the required type? BaseDataType (Variant) can
      * be anything... */
-    if(!compatibleDataType(server, &value->type->typeId, targetDataTypeId)) {
-        /* Convert to a matching value if possible */
-        if(!editableValue)
-            return UA_STATUSCODE_BADTYPEMISMATCH;
-        value = convertToMatchingValue(server, value, targetDataTypeId, editableValue);
-        if(!value)
-            return UA_STATUSCODE_BADTYPEMISMATCH;
-    }
+    if(!compatibleDataType(server, &value->type->typeId, targetDataTypeId))
+        return UA_STATUSCODE_BADTYPEMISMATCH;
 
     /* Array dimensions are checked later when writing the range */
     if(range)
@@ -676,34 +612,70 @@ typeCheckValue(UA_Server *server, const UA_NodeId *targetDataTypeId,
     return compatibleValueRankValue(targetValueRank, value);
 }
 
+/*****************/
+/* Write Service */
+/*****************/
+
+static void
+adjustValue(UA_Server *server, UA_Variant *value,
+            const UA_NodeId *targetDataTypeId) {
+    const UA_DataType *targetDataType = UA_findDataType(targetDataTypeId);
+    if(!targetDataType)
+        return;
+
+    /* A string is written to a byte array. the valuerank and array dimensions
+     * are checked later */
+    if(targetDataType == &UA_TYPES[UA_TYPES_BYTE] &&
+       value->type == &UA_TYPES[UA_TYPES_BYTESTRING] &&
+       UA_Variant_isScalar(value)) {
+        UA_ByteString *str = (UA_ByteString*)value->data;
+        value->type = &UA_TYPES[UA_TYPES_BYTE];
+        value->arrayLength = str->length;
+        value->data = str->data;
+        return;
+    }
+
+    /* An enum was sent as an int32, or an opaque type as a bytestring. This
+     * is detected with the typeIndex indicating the "true" datatype. */
+    enum type_equivalence te1 = typeEquivalence(targetDataType);
+    enum type_equivalence te2 = typeEquivalence(value->type);
+    if(te1 != TYPE_EQUIVALENCE_NONE && te1 == te2) {
+        value->type = targetDataType;
+        return;
+    }
+
+    /* No more possible equivalencies */
+}
+
+/* Stack layout: ... | node | type */
 static UA_StatusCode
 writeArrayDimensionsAttribute(UA_Server *server, UA_Session *session,
-                              UA_VariableNode *node, size_t arrayDimensionsSize,
-                              UA_UInt32 *arrayDimensions) {
+                              UA_VariableNode *node, const UA_VariableTypeNode *type,
+                              size_t arrayDimensionsSize, UA_UInt32 *arrayDimensions) {
+    UA_assert(node != NULL);
+    UA_assert(type != NULL);
+
     /* If this is a variabletype, there must be no instances or subtypes of it
      * when we do the change */
     if(node->nodeClass == UA_NODECLASS_VARIABLETYPE &&
-       UA_Node_hasSubTypeOrInstances((const UA_Node*)node))
+       UA_Node_hasSubTypeOrInstances((UA_Node*)node)) {
+        UA_LOG_INFO(server->config.logger, UA_LOGCATEGORY_SERVER,
+                    "Cannot change a variable type with existing instances");
         return UA_STATUSCODE_BADINTERNALERROR;
+    }
 
     /* Check that the array dimensions match with the valuerank */
-    UA_StatusCode retval =
-        compatibleValueRankArrayDimensions(node->valueRank, arrayDimensionsSize);
+    UA_StatusCode retval = compatibleValueRankArrayDimensions(node->valueRank, arrayDimensionsSize);
     if(retval != UA_STATUSCODE_GOOD) {
         UA_LOG_DEBUG(server->config.logger, UA_LOGCATEGORY_SERVER,
                      "The current value rank does not match the new array dimensions");
         return retval;
     }
 
-    /* Get the VariableType */
-    const UA_VariableTypeNode *vt = getVariableNodeType(server, (UA_VariableNode*)node);
-    if(!vt)
-        return UA_STATUSCODE_BADINTERNALERROR;
-
     /* Check if the array dimensions match with the wildcards in the
      * variabletype (dimension length 0) */
-    if(vt->arrayDimensions) {
-        retval = compatibleArrayDimensions(vt->arrayDimensionsSize, vt->arrayDimensions,
+    if(type->arrayDimensions) {
+        retval = compatibleArrayDimensions(type->arrayDimensionsSize, type->arrayDimensions,
                                            arrayDimensionsSize, arrayDimensions);
         if(retval != UA_STATUSCODE_GOOD) {
             UA_LOG_DEBUG(server->config.logger, UA_LOGCATEGORY_SERVER,
@@ -742,22 +714,18 @@ writeArrayDimensionsAttribute(UA_Server *server, UA_Session *session,
     return UA_STATUSCODE_GOOD;
 }
 
+/* Stack layout: ... | node | type */
 static UA_StatusCode
-writeValueRankAttributeWithVT(UA_Server *server, UA_Session *session,
-                              UA_VariableNode *node, UA_Int32 valueRank) {
-    const UA_VariableTypeNode *vt = getVariableNodeType(server, node);
-    if(!vt)
-        return UA_STATUSCODE_BADINTERNALERROR;
-    return writeValueRankAttribute(server, session, node,
-                                   valueRank, vt->valueRank);
-}
-
-UA_StatusCode
 writeValueRankAttribute(UA_Server *server, UA_Session *session,
-                        UA_VariableNode *node, UA_Int32 valueRank,
-                        UA_Int32 constraintValueRank) {
+                        UA_VariableNode *node, const UA_VariableTypeNode *type,
+                        UA_Int32 valueRank) {
+    UA_assert(node != NULL);
+    UA_assert(type != NULL);
+
+    UA_Int32 constraintValueRank = type->valueRank;
+
     /* If this is a variabletype, there must be no instances or subtypes of it
-       when we do the change */
+     * when we do the change */
     if(node->nodeClass == UA_NODECLASS_VARIABLETYPE &&
        UA_Node_hasSubTypeOrInstances((const UA_Node*)node))
         return UA_STATUSCODE_BADINTERNALERROR;
@@ -798,8 +766,11 @@ writeValueRankAttribute(UA_Server *server, UA_Session *session,
 
 static UA_StatusCode
 writeDataTypeAttribute(UA_Server *server, UA_Session *session,
-                       UA_VariableNode *node, const UA_NodeId *dataType,
-                       const UA_NodeId *constraintDataType) {
+                       UA_VariableNode *node, const UA_VariableTypeNode *type,
+                       const UA_NodeId *dataType) {
+    UA_assert(node != NULL);
+    UA_assert(type != NULL);
+
     /* If this is a variabletype, there must be no instances or subtypes of it
        when we do the change */
     if(node->nodeClass == UA_NODECLASS_VARIABLETYPE &&
@@ -807,7 +778,7 @@ writeDataTypeAttribute(UA_Server *server, UA_Session *session,
         return UA_STATUSCODE_BADINTERNALERROR;
 
     /* Does the new type match the constraints of the variabletype? */
-    if(!compatibleDataType(server, dataType, constraintDataType))
+    if(!compatibleDataType(server, dataType, &type->dataType))
         return UA_STATUSCODE_BADTYPEMISMATCH;
 
     /* Check if the current value would match the new type */
@@ -817,9 +788,9 @@ writeDataTypeAttribute(UA_Server *server, UA_Session *session,
     if(retval != UA_STATUSCODE_GOOD)
         return retval;
     if(value.hasValue) {
-        retval = typeCheckValue(server, dataType, node->valueRank,
-                                node->arrayDimensionsSize, node->arrayDimensions,
-                                &value.value, NULL, NULL);
+        retval = compatibleValue(server, dataType, node->valueRank,
+                                 node->arrayDimensionsSize, node->arrayDimensions,
+                                 &value.value, NULL);
         UA_DataValue_deleteMembers(&value);
         if(retval != UA_STATUSCODE_GOOD) {
             UA_LOG_DEBUG(server->config.logger, UA_LOGCATEGORY_SERVER,
@@ -839,24 +810,15 @@ writeDataTypeAttribute(UA_Server *server, UA_Session *session,
     return UA_STATUSCODE_GOOD;
 }
 
-static UA_StatusCode
-writeDataTypeAttributeWithVT(UA_Server *server, UA_Session *session,
-                             UA_VariableNode *node, const UA_NodeId *dataType) {
-    const UA_VariableTypeNode *vt = getVariableNodeType(server, node);
-    if(!vt)
-        return UA_STATUSCODE_BADINTERNALERROR;
-    return writeDataTypeAttribute(server, session, node, dataType, &vt->dataType);
-}
-
 static UA_StatusCode
 writeValueAttributeWithoutRange(UA_VariableNode *node, const UA_DataValue *value) {
-    UA_DataValue old_value = node->value.data.value; /* keep the pointers for restoring */
-    UA_StatusCode retval = UA_DataValue_copy(value, &node->value.data.value);
-    if(retval == UA_STATUSCODE_GOOD)
-        UA_DataValue_deleteMembers(&old_value);
-    else
-        node->value.data.value = old_value;
-    return retval;
+    UA_DataValue new_value;
+    UA_StatusCode retval = UA_DataValue_copy(value, &new_value);
+    if(retval != UA_STATUSCODE_GOOD)
+        return retval;
+    UA_DataValue_deleteMembers(&node->value.data.value);
+    node->value.data.value = new_value;
+    return UA_STATUSCODE_GOOD;
 }
 
 static UA_StatusCode
@@ -892,9 +854,13 @@ writeValueAttributeWithRange(UA_VariableNode *node, const UA_DataValue *value,
     return UA_STATUSCODE_GOOD;
 }
 
+/* Stack layout: ... | node */
 static UA_StatusCode
-writeValueAttribute(UA_Server *server, UA_Session *session, UA_VariableNode *node,
-                    const UA_DataValue *value, const UA_String *indexRange) {
+writeValueAttribute(UA_Server *server, UA_Session *session,
+                    UA_VariableNode *node, const UA_DataValue *value,
+                    const UA_String *indexRange) {
+    UA_assert(node != NULL);
+
     /* Parse the range */
     UA_NumericRange range;
     UA_NumericRange *rangeptr = NULL;
@@ -906,16 +872,16 @@ writeValueAttribute(UA_Server *server, UA_Session *session, UA_VariableNode *nod
         rangeptr = &range;
     }
 
-    /* Copy the value into an editable "container" where e.g. the datatype can
-     * be adjusted. The data itself is not written into. */
-    UA_DataValue editableValue = *value;
-    editableValue.value.storageType = UA_VARIANT_DATA_NODELETE;
+    /* Created an editable version. The data is not touched. Only the variant
+     * "container". */
+    UA_DataValue adjustedValue = *value;
 
     /* Type checking. May change the type of editableValue */
     if(value->hasValue) {
-        retval = typeCheckValue(server, &node->dataType, node->valueRank,
-                                node->arrayDimensionsSize, node->arrayDimensions,
-                                &value->value, rangeptr, &editableValue.value);
+        adjustValue(server, &adjustedValue.value, &node->dataType);
+        retval = compatibleValue(server, &node->dataType, node->valueRank,
+                                 node->arrayDimensionsSize, node->arrayDimensions,
+                                 &adjustedValue.value, rangeptr);
         if(retval != UA_STATUSCODE_GOOD) {
             if(rangeptr)
                 UA_free(range.dimensions);
@@ -924,43 +890,29 @@ writeValueAttribute(UA_Server *server, UA_Session *session, UA_VariableNode *nod
     }
 
     /* Set the source timestamp if there is none */
-    if(!editableValue.hasSourceTimestamp) {
-        editableValue.sourceTimestamp = UA_DateTime_now();
-        editableValue.hasSourceTimestamp = true;
+    if(!adjustedValue.hasSourceTimestamp) {
+        adjustedValue.sourceTimestamp = UA_DateTime_now();
+        adjustedValue.hasSourceTimestamp = true;
     }
 
     /* Ok, do it */
     if(node->valueSource == UA_VALUESOURCE_DATA) {
         if(!rangeptr)
-            retval = writeValueAttributeWithoutRange(node, &editableValue);
+            retval = writeValueAttributeWithoutRange(node, &adjustedValue);
         else
-            retval = writeValueAttributeWithRange(node, &editableValue, rangeptr);
+            retval = writeValueAttributeWithRange(node, &adjustedValue, rangeptr);
 
         /* Callback after writing */
-        if(retval == UA_STATUSCODE_GOOD && node->value.data.callback.onWrite) {
-            const UA_VariableNode *writtenNode;
-#ifdef UA_ENABLE_MULTITHREADING
-            /* Reopen the node to see the changes (multithreading only) */
-            writtenNode = (const UA_VariableNode*)
-                UA_NodeStore_get(server->nodestore, &node->nodeId);
-#else
-            writtenNode = node; /* The node is written in-situ (TODO: this might
-                                   change with the nodestore plugin approach) */
-#endif
-            UA_RCU_UNLOCK();
-            writtenNode->value.data.callback.onWrite(server, &session->sessionId,
-                                                     session->sessionHandle, &writtenNode->nodeId,
-                                                     writtenNode->context, rangeptr,
-                                                     &writtenNode->value.data.value);
-            UA_RCU_LOCK();
-        }
+        if(retval == UA_STATUSCODE_GOOD && node->value.data.callback.onWrite)
+            node->value.data.callback.onWrite(server, &session->sessionId,
+                                              session->sessionHandle, &node->nodeId,
+                                              node->context, rangeptr,
+                                              &adjustedValue);
     } else {
         if(node->value.dataSource.write) {
-            UA_RCU_UNLOCK();
             retval = node->value.dataSource.write(server, &session->sessionId,
                                                   session->sessionHandle, &node->nodeId,
-                                                  node->context, rangeptr, &editableValue);
-            UA_RCU_LOCK();
+                                                  node->context, rangeptr, &adjustedValue);
         } else {
             retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
         }
@@ -1025,6 +977,14 @@ writeIsAbstractAttribute(UA_Node *node, UA_Boolean value) {
         break;                                              \
     }
 
+#define GET_NODETYPE                                \
+    type = (const UA_VariableTypeNode*)             \
+        getNodeType(server, node);                  \
+    if(!type) {                                     \
+        retval = UA_STATUSCODE_BADTYPEMISMATCH;     \
+        break;                                      \
+    }
+
 /* This function implements the main part of the write service and operates on a
    copy of the node (not in single-threaded mode). */
 static UA_StatusCode
@@ -1034,6 +994,8 @@ copyAttributeIntoNode(UA_Server *server, UA_Session *session,
     UA_UInt32 userWriteMask = getUserWriteMask(server, session, node);
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
 
+    const UA_VariableTypeNode *type;
+
     switch(wvalue->attributeId) {
     case UA_ATTRIBUTEID_NODEID:
     case UA_ATTRIBUTEID_NODECLASS:
@@ -1116,23 +1078,29 @@ copyAttributeIntoNode(UA_Server *server, UA_Session *session,
         CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
         CHECK_USERWRITEMASK(UA_WRITEMASK_DATATYPE);
         CHECK_DATATYPE_SCALAR(NODEID);
-        retval = writeDataTypeAttributeWithVT(server, session, (UA_VariableNode*)node,
-                                              (const UA_NodeId*)value);
+        GET_NODETYPE
+        retval = writeDataTypeAttribute(server, session, (UA_VariableNode*)node,
+                                        type, (const UA_NodeId*)value);
+        UA_Nodestore_release(server, (const UA_Node*)type);
         break;
     case UA_ATTRIBUTEID_VALUERANK:
         CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
         CHECK_USERWRITEMASK(UA_WRITEMASK_VALUERANK);
         CHECK_DATATYPE_SCALAR(INT32);
-        retval = writeValueRankAttributeWithVT(server, session, (UA_VariableNode*)node,
-                                               *(const UA_Int32*)value);
+        GET_NODETYPE
+        retval = writeValueRankAttribute(server, session, (UA_VariableNode*)node,
+                                         type, *(const UA_Int32*)value);
+        UA_Nodestore_release(server, (const UA_Node*)type);
         break;
     case UA_ATTRIBUTEID_ARRAYDIMENSIONS:
         CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
         CHECK_USERWRITEMASK(UA_WRITEMASK_ARRRAYDIMENSIONS);
         CHECK_DATATYPE_ARRAY(UINT32);
+        GET_NODETYPE
         retval = writeArrayDimensionsAttribute(server, session, (UA_VariableNode*)node,
-                                               wvalue->value.value.arrayLength,
+                                               type, wvalue->value.value.arrayLength,
                                                (UA_UInt32 *)wvalue->value.value.data);
+        UA_Nodestore_release(server, (const UA_Node*)type);
         break;
     case UA_ATTRIBUTEID_ACCESSLEVEL:
         CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE);

+ 119 - 64
src/server/ua_services_call.c

@@ -16,20 +16,23 @@ getArgumentsVariableNode(UA_Server *server, const UA_MethodNode *ofMethod,
 
         if(rk->isInverse != false)
             continue;
-        
+
         if(!UA_NodeId_equal(&hasProperty, &rk->referenceTypeId))
             continue;
 
         for(size_t j = 0; j < rk->targetIdsSize; ++j) {
             const UA_Node *refTarget =
-                UA_NodeStore_get(server->nodestore, &rk->targetIds[j].nodeId);
+                server->config.nodestore.getNode(server->config.nodestore.context,
+                                                 &rk->targetIds[j].nodeId);
             if(!refTarget)
                 continue;
             if(refTarget->nodeClass == UA_NODECLASS_VARIABLE &&
                refTarget->browseName.namespaceIndex == 0 &&
                UA_String_equal(&withBrowseName, &refTarget->browseName.name)) {
-                return (const UA_VariableNode*) refTarget;
+                return (const UA_VariableNode*)refTarget;
             }
+            server->config.nodestore.releaseNode(server->config.nodestore.context,
+                                                 refTarget);
         }
     }
     return NULL;
@@ -53,48 +56,59 @@ argumentsConformsToDefinition(UA_Server *server, const UA_VariableNode *argRequi
 
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
     for(size_t i = 0; i < argReqsSize; ++i)
-        retval |= typeCheckValue(server, &argReqs[i].dataType, argReqs[i].valueRank,
-                                 argReqs[i].arrayDimensionsSize, argReqs[i].arrayDimensions,
-                                 &args[i], NULL, &args[i]);
+        retval |= compatibleValue(server, &argReqs[i].dataType, argReqs[i].valueRank,
+                                  argReqs[i].arrayDimensionsSize, argReqs[i].arrayDimensions,
+                                  &args[i], NULL);
+    return retval;
+}
+
+static UA_StatusCode
+validMethodArguments(UA_Server *server, const UA_MethodNode *method,
+                     const UA_CallMethodRequest *request) {
+    /* Get the input arguments node */
+    const UA_VariableNode *inputArguments =
+        getArgumentsVariableNode(server, method, UA_STRING("InputArguments"));
+    UA_StatusCode retval = UA_STATUSCODE_GOOD;
+    if(!inputArguments) {
+        if(request->inputArgumentsSize > 0)
+            retval = UA_STATUSCODE_BADINVALIDARGUMENT;
+        return retval;
+    }
+
+    /* Verify the request */
+    retval = argumentsConformsToDefinition(server, inputArguments,
+                                           request->inputArgumentsSize,
+                                           request->inputArguments);
+
+    /* Release the input arguments node */
+    server->config.nodestore.releaseNode(server->config.nodestore.context,
+                                         (const UA_Node*)inputArguments);
     return retval;
 }
 
+static const UA_NodeId hasComponentNodeId = {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_HASCOMPONENT}};
+static const UA_NodeId hasSubTypeNodeId = {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_HASSUBTYPE}};
+
 static void
-Operation_CallMethod(UA_Server *server, UA_Session *session,
-                     const UA_CallMethodRequest *request,
-                     UA_CallMethodResult *result) {
-    /* Verify the method node */
-    const UA_MethodNode *methodCalled = (const UA_MethodNode*)
-        UA_NodeStore_get(server->nodestore, &request->methodId);
-    if(!methodCalled)
-        result->statusCode = UA_STATUSCODE_BADMETHODINVALID;
-    else if(methodCalled->nodeClass != UA_NODECLASS_METHOD)
+callWithMethodAndObject(UA_Server *server, UA_Session *session,
+                        const UA_CallMethodRequest *request, UA_CallMethodResult *result,
+                        const UA_MethodNode *method, const UA_ObjectNode *object) {
+    /* Verify the object's NodeClass */
+    if(object->nodeClass != UA_NODECLASS_OBJECT &&
+       object->nodeClass != UA_NODECLASS_OBJECTTYPE) {
         result->statusCode = UA_STATUSCODE_BADNODECLASSINVALID;
-    else if(!methodCalled->method)
-        result->statusCode = UA_STATUSCODE_BADINTERNALERROR;
-    if(result->statusCode != UA_STATUSCODE_GOOD)
         return;
+    }
 
-    /* Verify the object node */
-    const UA_ObjectNode *object = (const UA_ObjectNode*)
-        UA_NodeStore_get(server->nodestore, &request->objectId);
-    if(!object)
-        result->statusCode = UA_STATUSCODE_BADNODEIDINVALID;
-    else if(object->nodeClass != UA_NODECLASS_OBJECT &&
-            object->nodeClass != UA_NODECLASS_OBJECTTYPE)
+    /* Verify the method's NodeClass */
+    if(method->nodeClass != UA_NODECLASS_METHOD) {
         result->statusCode = UA_STATUSCODE_BADNODECLASSINVALID;
-    if(result->statusCode != UA_STATUSCODE_GOOD)
         return;
+    }
 
-    /* Verify access rights */
-    UA_Boolean executable = methodCalled->executable;
-    if(session != &adminSession)
-        executable = executable &&
-            server->config.accessControl.getUserExecutableOnObject(&session->sessionId,
-                           session->sessionHandle, &request->methodId, methodCalled->context,
-                           &request->objectId, object->context);
-    if(!executable) {
-        result->statusCode = UA_STATUSCODE_BADNOTWRITABLE; // There is no NOTEXECUTABLE?
+    /* Is there a method to execute? */
+    if(!method->method) {
+        result->statusCode = UA_STATUSCODE_BADINTERNALERROR;
         return;
     }
 
@@ -103,13 +117,11 @@ Operation_CallMethod(UA_Server *server, UA_Session *session,
      * every reference between the parent object and the method node if there is
      * a hasComponent (or subtype) reference */
     UA_Boolean found = false;
-    UA_NodeId hasComponentNodeId = UA_NODEID_NUMERIC(0,UA_NS0ID_HASCOMPONENT);
-    UA_NodeId hasSubTypeNodeId = UA_NODEID_NUMERIC(0,UA_NS0ID_HASSUBTYPE);
     for(size_t i = 0; i < object->referencesSize; ++i) {
         UA_NodeReferenceKind *rk = &object->references[i];
         if(rk->isInverse)
             continue;
-        if(!isNodeInTree(server->nodestore, &rk->referenceTypeId,
+        if(!isNodeInTree(&server->config.nodestore, &rk->referenceTypeId,
                          &hasComponentNodeId, &hasSubTypeNodeId, 1))
             continue;
         for(size_t j = 0; j < rk->targetIdsSize; ++j) {
@@ -124,45 +136,88 @@ Operation_CallMethod(UA_Server *server, UA_Session *session,
         return;
     }
 
-    /* Verify Input Argument count, types and sizes */
-    const UA_VariableNode *inputArguments =
-        getArgumentsVariableNode(server, methodCalled, UA_STRING("InputArguments"));
-    if(!inputArguments) {
-        if(request->inputArgumentsSize > 0)
-            result->statusCode = UA_STATUSCODE_BADINVALIDARGUMENT;
-    } else {
-        result->statusCode = argumentsConformsToDefinition(server, inputArguments,
-                                                           request->inputArgumentsSize,
-                                                           request->inputArguments);
+    /* Verify access rights */
+    UA_Boolean executable = method->executable;
+    if(session != &adminSession)
+        executable = executable &&
+            server->config.accessControl.getUserExecutableOnObject(&session->sessionId,
+                           session->sessionHandle, &request->methodId, method->context,
+                           &request->objectId, object->context);
+    if(!executable) {
+        result->statusCode = UA_STATUSCODE_BADNOTWRITABLE; // There is no NOTEXECUTABLE?
+        return;
     }
+
+    /* Verify Input Arguments */
+    result->statusCode = validMethodArguments(server, method, request);
     if(result->statusCode != UA_STATUSCODE_GOOD)
         return;
 
-    /* Allocate the output arguments */
-    result->outputArgumentsSize = 0; /* the default */
+    /* Get the output arguments node */
     const UA_VariableNode *outputArguments =
-        getArgumentsVariableNode(server, methodCalled, UA_STRING("OutputArguments"));
+        getArgumentsVariableNode(server, method, UA_STRING("OutputArguments"));
+
+    /* Allocate the output arguments array */
     if(outputArguments) {
-        result->outputArguments =
-            (UA_Variant*)UA_Array_new(outputArguments->value.data.value.value.arrayLength,
-                                      &UA_TYPES[UA_TYPES_VARIANT]);
-        if(!result->outputArguments) {
-            result->statusCode = UA_STATUSCODE_BADOUTOFMEMORY;
-            return;
+        if(outputArguments->value.data.value.value.arrayLength > 0) {
+            result->outputArguments = (UA_Variant*)
+                UA_Array_new(outputArguments->value.data.value.value.arrayLength,
+                             &UA_TYPES[UA_TYPES_VARIANT]);
+            if(!result->outputArguments) {
+                result->statusCode = UA_STATUSCODE_BADOUTOFMEMORY;
+                return;
+            }
+            result->outputArgumentsSize = outputArguments->value.data.value.value.arrayLength;
         }
-        result->outputArgumentsSize = outputArguments->value.data.value.value.arrayLength;
+
+        /* Release the output arguments node */
+        server->config.nodestore.releaseNode(server->config.nodestore.context,
+                                             (const UA_Node*)outputArguments);
     }
 
     /* Call the method */
-    result->statusCode =
-        methodCalled->method(server, &session->sessionId, session->sessionHandle,
-                             &methodCalled->nodeId, (void*)(uintptr_t)methodCalled->context,
-                             &object->nodeId, (void*)(uintptr_t)&object->context,
-                             request->inputArgumentsSize, request->inputArguments,
-                             result->outputArgumentsSize, result->outputArguments);
+    result->statusCode = method->method(server, &session->sessionId, session->sessionHandle,
+                                        &method->nodeId, (void*)(uintptr_t)method->context,
+                                        &object->nodeId, (void*)(uintptr_t)&object->context,
+                                        request->inputArgumentsSize, request->inputArguments,
+                                        result->outputArgumentsSize, result->outputArguments);
     /* TODO: Verify Output matches the argument definition */
 }
 
+static void
+Operation_CallMethod(UA_Server *server, UA_Session *session,
+                     const UA_CallMethodRequest *request,
+                     UA_CallMethodResult *result) {
+    /* Get the method node */
+    const UA_MethodNode *method = (const UA_MethodNode*)
+        server->config.nodestore.getNode(server->config.nodestore.context,
+                                         &request->methodId);
+    if(!method) {
+        result->statusCode = UA_STATUSCODE_BADMETHODINVALID;
+        return;
+    }
+
+    /* Get the object node */
+    const UA_ObjectNode *object = (const UA_ObjectNode*)
+        server->config.nodestore.getNode(server->config.nodestore.context,
+                                         &request->objectId);
+    if(!object) {
+        result->statusCode = UA_STATUSCODE_BADNODEIDINVALID;
+        server->config.nodestore.releaseNode(server->config.nodestore.context,
+                                             (const UA_Node*)method);
+        return;
+    }
+
+    /* Continue with method and object as context */
+    callWithMethodAndObject(server, session, request, result, method, object);
+
+    /* Release the method and object node */
+    server->config.nodestore.releaseNode(server->config.nodestore.context,
+                                         (const UA_Node*)method);
+    server->config.nodestore.releaseNode(server->config.nodestore.context,
+                                         (const UA_Node*)object);
+}
+
 void Service_Call(UA_Server *server, UA_Session *session,
                   const UA_CallRequest *request,
                   UA_CallResponse *response) {

Разлика између датотеке није приказан због своје велике величине
+ 410 - 430
src/server/ua_services_nodemanagement.c


+ 7 - 5
src/server/ua_services_subscription.c

@@ -158,10 +158,13 @@ setMonitoredItemSettings(UA_Server *server, UA_MonitoredItem *mon,
     UA_Double samplingInterval = params->samplingInterval;
     if(mon->attributeID == UA_ATTRIBUTEID_VALUE) {
         const UA_VariableNode *vn = (const UA_VariableNode*)
-            UA_NodeStore_get(server->nodestore, &mon->monitoredNodeId);
-        if(vn && vn->nodeClass == UA_NODECLASS_VARIABLE &&
-           samplingInterval <  vn->minimumSamplingInterval)
-            samplingInterval = vn->minimumSamplingInterval;
+            UA_Nodestore_get(server, &mon->monitoredNodeId);
+        if(vn) {
+            if(vn->nodeClass == UA_NODECLASS_VARIABLE &&
+               samplingInterval <  vn->minimumSamplingInterval)
+                samplingInterval = vn->minimumSamplingInterval;
+            UA_Nodestore_release(server, (const UA_Node*)vn);
+        }
     } else if(mon->attributeID == UA_ATTRIBUTEID_EVENTNOTIFIER) {
         /* TODO: events should not need a samplinginterval */
         samplingInterval = 10000.0f; // 10 seconds to reduce the load
@@ -195,7 +198,6 @@ setMonitoredItemSettings(UA_Server *server, UA_MonitoredItem *mon,
 }
 
 static const UA_String binaryEncoding = {sizeof("Default Binary")-1, (UA_Byte*)"Default Binary"};
-/* static const UA_String xmlEncoding = {sizeof("Default Xml")-1, (UA_Byte*)"Default Xml"}; */
 
 /* Thread-local variables to pass additional arguments into the operation */
 static UA_THREAD_LOCAL UA_Subscription *op_sub;

+ 66 - 44
src/server/ua_services_view.c

@@ -5,10 +5,11 @@
 #include "ua_server_internal.h"
 #include "ua_services.h"
 
+/* Target node on top of the stack */
 static UA_StatusCode
 fillReferenceDescription(UA_Server *server, const UA_Node *curr,
-                         const UA_NodeReferenceKind *ref, UA_UInt32 mask,
-                         UA_ReferenceDescription *descr) {
+                         const UA_NodeReferenceKind *ref,
+                         UA_UInt32 mask, UA_ReferenceDescription *descr) {
     UA_ReferenceDescription_init(descr);
     UA_StatusCode retval = UA_NodeId_copy(&curr->nodeId, &descr->nodeId.nodeId);
     if(mask & UA_BROWSERESULTMASK_REFERENCETYPEID)
@@ -24,8 +25,11 @@ fillReferenceDescription(UA_Server *server, const UA_Node *curr,
     if(mask & UA_BROWSERESULTMASK_TYPEDEFINITION) {
         if(curr->nodeClass == UA_NODECLASS_OBJECT ||
            curr->nodeClass == UA_NODECLASS_VARIABLE) {
-            retval |= UA_NodeId_copy(getNodeType(server, curr),
-                                     &descr->typeDefinition.nodeId);
+            const UA_Node *type = getNodeType(server, curr);
+            if(type) {
+                retval |= UA_NodeId_copy(&type->nodeId, &descr->typeDefinition.nodeId);
+                UA_Nodestore_release(server, type);
+            }
         }
     }
     return retval;
@@ -47,22 +51,16 @@ relevantReference(UA_Server *server, UA_Boolean includeSubtypes,
         return UA_NodeId_equal(rootRef, testRef);
 
     const UA_NodeId hasSubType = UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE);
-    return isNodeInTree(server->nodestore, testRef, rootRef, &hasSubType, 1);
+    return isNodeInTree(&server->config.nodestore, testRef, rootRef, &hasSubType, 1);
 }
 
 /* Returns whether the node / continuationpoint is done */
 static UA_Boolean
-browseReferences(UA_Server *server, const UA_BrowseDescription *descr,
+browseReferences(UA_Server *server, const UA_Node *node,
+                 const UA_BrowseDescription *descr,
                  UA_BrowseResult *result, ContinuationPointEntry *cp) {
     UA_assert(cp != NULL);
 
-    /* Get the node */
-    const UA_Node *node = UA_NodeStore_get(server->nodestore, &descr->nodeId);
-    if(!node) {
-        result->statusCode = UA_STATUSCODE_BADNODEIDUNKNOWN;
-        return true;;
-    }
-
     /* If the node has no references, just return */
     if(node->referencesSize == 0) {
         result->referencesSize = 0;
@@ -79,9 +77,8 @@ browseReferences(UA_Server *server, const UA_BrowseDescription *descr,
 
     /* Allocate the results array */
     size_t refs_size = 2; /* True size of the array */
-    result->references =
-        (UA_ReferenceDescription*)UA_Array_new(refs_size,
-                          &UA_TYPES[UA_TYPES_REFERENCEDESCRIPTION]);
+    result->references = (UA_ReferenceDescription*)
+        UA_Array_new(refs_size, &UA_TYPES[UA_TYPES_REFERENCEDESCRIPTION]);
     if(!result->references) {
         result->statusCode = UA_STATUSCODE_BADOUTOFMEMORY;
         return false;
@@ -108,39 +105,45 @@ browseReferences(UA_Server *server, const UA_BrowseDescription *descr,
         /* Loop over the targets */
         for(; targetIndex < rk->targetIdsSize; ++targetIndex) {
             /* Get the node */
-            const UA_Node *target = UA_NodeStore_get(server->nodestore,
-                                                     &rk->targetIds[targetIndex].nodeId);
+            const UA_Node *target = UA_Nodestore_get(server, &rk->targetIds[targetIndex].nodeId);
+            if(!target)
+                continue;
 
             /* Test if the node class matches */
-            if(!target || (descr->nodeClassMask != 0 &&
-                           (target->nodeClass & descr->nodeClassMask) == 0))
+            if(descr->nodeClassMask != 0 && (target->nodeClass & descr->nodeClassMask) == 0) {
+                UA_Nodestore_release(server, target);
                 continue;
+            }
 
             /* A match! Can we return it? */
             if(result->referencesSize >= maxrefs) {
                 /* There are references we could not return */
                 cp->referenceKindIndex = referenceKindIndex;
                 cp->targetIndex = targetIndex;
+                UA_Nodestore_release(server, target);
                 return false;
             }
 
             /* Make enough space in the array */
             if(result->referencesSize >= refs_size) {
                 refs_size *= 2;
-                UA_ReferenceDescription *rd =
-                    (UA_ReferenceDescription*)UA_realloc(result->references,
-                               sizeof(UA_ReferenceDescription) * refs_size);
+                UA_ReferenceDescription *rd = (UA_ReferenceDescription*)
+                    UA_realloc(result->references, sizeof(UA_ReferenceDescription) * refs_size);
                 if(!rd) {
                     result->statusCode = UA_STATUSCODE_BADOUTOFMEMORY;
+                    UA_Nodestore_release(server, target);
                     goto error_recovery;
                 }
                 result->references = rd;
             }
 
-            /* Copy the node description */
+            /* Copy the node description. Target is on top of the stack */
             result->statusCode =
                 fillReferenceDescription(server, target, rk, descr->resultMask,
                                          &result->references[result->referencesSize]);
+
+            UA_Nodestore_release(server, target);
+
             if(result->statusCode != UA_STATUSCODE_GOOD)
                 goto error_recovery;
 
@@ -209,15 +212,31 @@ Service_Browse_single(UA_Server *server, UA_Session *session,
 
     /* Is the reference type valid? */
     if(!UA_NodeId_isNull(&descr->referenceTypeId)) {
-        const UA_Node *reftype = UA_NodeStore_get(server->nodestore, &descr->referenceTypeId);
-        if(!reftype || reftype->nodeClass != UA_NODECLASS_REFERENCETYPE) {
+        const UA_Node *reftype = UA_Nodestore_get(server, &descr->referenceTypeId);
+        if(!reftype) {
+            result->statusCode = UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
+            return;
+        }
+
+        UA_Boolean isRef = (reftype->nodeClass == UA_NODECLASS_REFERENCETYPE);
+        UA_Nodestore_release(server, reftype);
+
+        if(!isRef) {
             result->statusCode = UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
             return;
         }
     }
 
+    const UA_Node *node = UA_Nodestore_get(server, &descr->nodeId);
+    if(!node) {
+        result->statusCode = UA_STATUSCODE_BADNODEIDUNKNOWN;
+        return;
+    }
+
     /* Browse the references */
-    UA_Boolean done = browseReferences(server, descr, result, internal_cp);
+    UA_Boolean done = browseReferences(server, node, descr, result, internal_cp);
+
+    UA_Nodestore_release(server, node);
 
     /* Exit early if an error occured */
     if(result->statusCode != UA_STATUSCODE_GOOD)
@@ -226,8 +245,7 @@ Service_Browse_single(UA_Server *server, UA_Session *session,
     /* A continuation point exists already */
     if(cp) {
         if(done) {
-            /* All done, remove a finished continuationPoint */
-            removeCp(cp, session);
+            removeCp(cp, session); /* All done, remove a finished continuationPoint */
         } else {
             /* Return the cp identifier */
             UA_ByteString_copy(&cp->identifier, &result->continuationPoint);
@@ -298,10 +316,8 @@ UA_Server_browse(UA_Server *server, UA_UInt32 maxrefs,
                  const UA_BrowseDescription *descr) {
     UA_BrowseResult result;
     UA_BrowseResult_init(&result);
-    UA_RCU_LOCK();
     Service_Browse_single(server, &adminSession, NULL,
                           descr, maxrefs, &result);
-    UA_RCU_UNLOCK();
     return result;
 }
 
@@ -351,10 +367,8 @@ UA_Server_browseNext(UA_Server *server, UA_Boolean releaseContinuationPoint,
     UA_BrowseResult result;
     UA_BrowseResult_init(&result);
     op_releaseContinuationPoint = releaseContinuationPoint;
-    UA_RCU_LOCK();
     Operation_BrowseNext(server, &adminSession,
                          continuationPoint, &result);
-    UA_RCU_UNLOCK();
     return result;
 }
 
@@ -419,15 +433,19 @@ walkBrowsePathElement(UA_Server *server, UA_Session *session,
     /* Return all references? */
     UA_Boolean all_refs = UA_NodeId_isNull(&elem->referenceTypeId);
     if(!all_refs) {
-        const UA_Node *rootRef = UA_NodeStore_get(server->nodestore, &elem->referenceTypeId);
-        if(!rootRef || rootRef->nodeClass != UA_NODECLASS_REFERENCETYPE)
+        const UA_Node *rootRef = UA_Nodestore_get(server, &elem->referenceTypeId);
+        if(!rootRef)
+            return;
+        UA_Boolean match = (rootRef->nodeClass == UA_NODECLASS_REFERENCETYPE);
+        UA_Nodestore_release(server, rootRef);
+        if(!match)
             return;
     }
 
     /* Iterate over all nodes at the current depth-level */
     for(size_t i = 0; i < currentCount; ++i) {
         /* Get the node */
-        const UA_Node *node = UA_NodeStore_get(server->nodestore, &current[i]);
+        const UA_Node *node = UA_Nodestore_get(server, &current[i]);
         if(!node) {
             /* If we cannot find the node at depth 0, the starting node does not exist */
             if(elemDepth == 0)
@@ -438,8 +456,10 @@ walkBrowsePathElement(UA_Server *server, UA_Session *session,
         /* Test whether the current node has the target name required in the
          * previous path element */
         if(targetName && (targetName->namespaceIndex != node->browseName.namespaceIndex ||
-                          !UA_String_equal(&targetName->name, &node->browseName.name)))
+                          !UA_String_equal(&targetName->name, &node->browseName.name))) {
+            UA_Nodestore_release(server, node);
             continue;
+        }
 
         /* Loop over the nodes references */
         for(size_t r = 0; r < node->referencesSize &&
@@ -459,6 +479,8 @@ walkBrowsePathElement(UA_Server *server, UA_Session *session,
             walkBrowsePathElementReferenceTargets(result, targetsSize, next, nextSize,
                                                   nextCount, elemDepth, rk);
         }
+
+        UA_Nodestore_release(server, node);
     }
 }
 
@@ -468,8 +490,7 @@ addBrowsePathTargets(UA_Server *server, UA_Session *session,
                      UA_BrowsePathResult *result, const UA_QualifiedName *targetName,
                      UA_NodeId *current, size_t currentCount) {
     for(size_t i = 0; i < currentCount; i++) {
-        /* Get the node */
-        const UA_Node *node = UA_NodeStore_get(server->nodestore, &current[i]);
+        const UA_Node *node = UA_Nodestore_get(server, &current[i]);
         if(!node) {
             UA_NodeId_deleteMembers(&current[i]);
             continue;
@@ -477,8 +498,12 @@ addBrowsePathTargets(UA_Server *server, UA_Session *session,
 
         /* Test whether the current node has the target name required in the
          * previous path element */
-        if(targetName->namespaceIndex != node->browseName.namespaceIndex ||
-           !UA_String_equal(&targetName->name, &node->browseName.name)) {
+        UA_Boolean valid = targetName->namespaceIndex == node->browseName.namespaceIndex &&
+            UA_String_equal(&targetName->name, &node->browseName.name);
+
+        UA_Nodestore_release(server, node);
+
+        if(!valid) {
             UA_NodeId_deleteMembers(&current[i]);
             continue;
         }
@@ -644,9 +669,7 @@ UA_Server_translateBrowsePathToNodeIds(UA_Server *server,
                                        const UA_BrowsePath *browsePath) {
     UA_BrowsePathResult result;
     UA_BrowsePathResult_init(&result);
-    UA_RCU_LOCK();
     Operation_TranslateBrowsePathToNodeIds(server, &adminSession, browsePath, &result);
-    UA_RCU_UNLOCK();
     return result;
 }
 
@@ -656,7 +679,6 @@ Service_TranslateBrowsePathsToNodeIds(UA_Server *server, UA_Session *session,
                                       UA_TranslateBrowsePathsToNodeIdsResponse *response) {
     UA_LOG_DEBUG_SESSION(server->config.logger, session,
                          "Processing TranslateBrowsePathsToNodeIdsRequest");
-
     response->responseHeader.serviceResult = 
         UA_Server_processServiceOperations(server, session,
                   (UA_ServiceOperation)Operation_TranslateBrowsePathToNodeIds,

+ 1 - 0
tests/CMakeLists.txt

@@ -28,6 +28,7 @@ set(test_plugin_sources ${PROJECT_SOURCE_DIR}/plugins/ua_network_tcp.c
                         ${PROJECT_SOURCE_DIR}/plugins/ua_log_stdout.c
                         ${PROJECT_SOURCE_DIR}/plugins/ua_config_default.c
                         ${PROJECT_SOURCE_DIR}/plugins/ua_accesscontrol_default.c
+                        ${PROJECT_SOURCE_DIR}/plugins/ua_nodestore_default.c
                         ${PROJECT_SOURCE_DIR}/tests/testing_clock.c)
 
 add_library(open62541-testplugins OBJECT ${test_plugin_sources})

+ 1 - 1
tests/check_accesscontrol.c

@@ -8,7 +8,7 @@
 #include "ua_types.h"
 #include "ua_server.h"
 #include "ua_client.h"
-#include "ua_config_standard.h"
+#include "ua_config_default.h"
 #include "check.h"
 
 UA_Server *server;

+ 9 - 11
tests/check_client_highlevel.c

@@ -210,18 +210,19 @@ START_TEST(Node_Add) {
         ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
     }
 
+    UA_ExpandedNodeId target = UA_EXPANDEDNODEID_NULL;
+    target.nodeId = newObjectId;
+
     // Add 'Top' to view
     retval = UA_Client_addReference(client, newViewId, UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
-                                    UA_TRUE, UA_STRING_NULL,
-                                    UA_EXPANDEDNODEID_NUMERIC(1, newObjectId.identifier.numeric),
-                                    UA_NODECLASS_VARIABLE);
+                                    UA_TRUE, UA_STRING_NULL, target, UA_NODECLASS_VARIABLE);
 
     ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
 
     // Delete 'Top' from view
     retval = UA_Client_deleteReference(client, newViewId,
-                                       UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), UA_TRUE,
-                                       UA_EXPANDEDNODEID_NUMERIC(1, newObjectId.identifier.numeric), UA_TRUE);
+                                       UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
+                                       true, target, true);
     ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
 
     // Delete 'AllTopCoordinates' view
@@ -817,14 +818,12 @@ START_TEST(Node_ReadWrite_DataType) {
     UA_NodeId dataType;
 
     // read to check if node id was changed
-    UA_StatusCode retval = UA_Client_readDataTypeAttribute(client, nodeReadWriteGeneric, &dataType);
+    UA_StatusCode retval = UA_Client_readDataTypeAttribute(client, nodeReadWriteInt, &dataType);
     ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
 
-    ck_assert_int_eq(dataType.identifierType, UA_NODEIDTYPE_NUMERIC);
-    ck_assert_int_eq(dataType.namespaceIndex, 0);
-    ck_assert_int_eq(dataType.identifier.numeric, UA_NS0ID_BASEDATATYPE);
+    ck_assert(UA_NodeId_equal(&dataType, &UA_TYPES[UA_TYPES_INT32].typeId));
 
-    UA_NodeId newDataType = UA_TYPES[UA_TYPES_UINT32].typeId;
+    UA_NodeId newDataType = UA_TYPES[UA_TYPES_VARIANT].typeId;
     retval = UA_Client_writeDataTypeAttribute(client, nodeReadWriteGeneric, &newDataType);
     ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
 
@@ -856,7 +855,6 @@ END_TEST
 
 
 START_TEST(Node_ReadWrite_ArrayDimensions) {
-
     UA_UInt32 *arrayDimsRead;
     size_t arrayDimsReadSize;
     UA_StatusCode retval = UA_Client_readArrayDimensionsAttribute(client, nodeReadWriteGeneric,

+ 91 - 78
tests/check_nodestore.c

@@ -7,37 +7,53 @@
 #include <time.h>
 
 #include "ua_types.h"
-#include "server/ua_nodestore.h"
-#include "server/ua_server_internal.h"
+#include "ua_plugin_nodestore.h"
+#include "ua_nodestore_default.h"
 #include "ua_util.h"
 #include "check.h"
 
+/* container_of */
+#define container_of(ptr, type, member) \
+    (type *)((uintptr_t)ptr - offsetof(type,member))
+
 #ifdef UA_ENABLE_MULTITHREADING
 #include <pthread.h>
-#include <urcu.h>
 #endif
 
-UA_NodeStore *ns;
+/* Dirty redifinition from ua_nodestore_default.c to check that all nodes were
+ * released */
+typedef struct UA_NodeMapEntry {
+    struct UA_NodeMapEntry *orig; /* the version this is a copy from (or NULL) */
+    UA_UInt16 refCount; /* How many consumers have a reference to the node? */
+    UA_Boolean deleted; /* Node was marked as deleted and can be deleted when refCount == 0 */
+    UA_Node node;
+} UA_NodeMapEntry;
+
+static void checkAllReleased(void *context, const UA_Node* node) {
+    UA_NodeMapEntry *entry = container_of(node, UA_NodeMapEntry, node);
+    ck_assert_int_eq(entry->refCount, 1); /* The count is increased when the visited node is checked out */
+}
+
+UA_Nodestore ns;
 
 static void setup(void) {
-    ns = UA_NodeStore_new();
-    UA_RCU_LOCK();
+    UA_Nodestore_default_new(&ns);
 }
 
 static void teardown(void) {
-    UA_NodeStore_delete(ns);
-    UA_RCU_UNLOCK();
+    ns.iterate(ns.context, NULL, checkAllReleased);
+    ns.deleteNodestore(ns.context);
 }
 
-int zeroCnt = 0;
-int visitCnt = 0;
-static void checkZeroVisitor(const UA_Node* node) {
+static int zeroCnt = 0;
+static int visitCnt = 0;
+static void checkZeroVisitor(void *context, const UA_Node* node) {
     visitCnt++;
     if (node == NULL) zeroCnt++;
 }
 
 static UA_Node* createNode(UA_Int16 nsid, UA_Int32 id) {
-    UA_Node *p = (UA_Node *)UA_NodeStore_newVariableNode();
+    UA_Node *p = ns.newNode(ns.context, UA_NODECLASS_VARIABLE);
     p->nodeId.identifierType = UA_NODEIDTYPE_NUMERIC;
     p->nodeId.namespaceIndex = nsid;
     p->nodeId.identifier.numeric = id;
@@ -47,86 +63,91 @@ static UA_Node* createNode(UA_Int16 nsid, UA_Int32 id) {
 
 START_TEST(replaceExistingNode) {
     UA_Node* n1 = createNode(0,2253);
-    UA_NodeStore_insert(ns, n1);
+    ns.insertNode(ns.context, n1, NULL);
     UA_NodeId in1 = UA_NODEID_NUMERIC(0, 2253);
-    UA_Node* n2 = UA_NodeStore_getCopy(ns, &in1);
-    UA_StatusCode retval = UA_NodeStore_replace(ns, n2);
+    UA_Node* n2;
+    ns.getNodeCopy(ns.context, &in1, &n2);
+    UA_StatusCode retval = ns.replaceNode(ns.context, n2);
     ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
 }
 END_TEST
 
 START_TEST(replaceOldNode) {
     UA_Node* n1 = createNode(0,2253);
-    UA_NodeStore_insert(ns, n1);
+    ns.insertNode(ns.context, n1, NULL);
     UA_NodeId in1 = UA_NODEID_NUMERIC(0,2253);
-    UA_Node* n2 = UA_NodeStore_getCopy(ns, &in1);
-    UA_Node* n3 = UA_NodeStore_getCopy(ns, &in1);
+    UA_Node* n2;
+    UA_Node* n3;
+    ns.getNodeCopy(ns.context, &in1, &n2);
+    ns.getNodeCopy(ns.context, &in1, &n3);
 
     /* shall succeed */
-    UA_StatusCode retval = UA_NodeStore_replace(ns, n2);
+    UA_StatusCode retval = ns.replaceNode(ns.context, n2);
     ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
 
     /* shall fail */
-    retval = UA_NodeStore_replace(ns, n3);
+    retval = ns.replaceNode(ns.context, n3);
     ck_assert_int_ne(retval, UA_STATUSCODE_GOOD);
 }
 END_TEST
 
 START_TEST(findNodeInUA_NodeStoreWithSingleEntry) {
     UA_Node* n1 = createNode(0,2253);
-    UA_NodeStore_insert(ns, n1);
+    ns.insertNode(ns.context, n1, NULL);
     UA_NodeId in1 = UA_NODEID_NUMERIC(0,2253);
-    const UA_Node* nr = UA_NodeStore_get(ns, &in1);
+    const UA_Node* nr = ns.getNode(ns.context, &in1);
     ck_assert_int_eq((uintptr_t)n1, (uintptr_t)nr);
+    ns.releaseNode(ns.context, nr);
 }
 END_TEST
 
 START_TEST(failToFindNodeInOtherUA_NodeStore) {
     UA_Node* n1 = createNode(0,2255);
-    UA_NodeStore_insert(ns, n1);
+    ns.insertNode(ns.context, n1, NULL);
     UA_NodeId in1 = UA_NODEID_NUMERIC(1, 2255);
-    const UA_Node* nr = UA_NodeStore_get(ns, &in1);
+    const UA_Node* nr = ns.getNode(ns.context, &in1);
     ck_assert_int_eq((uintptr_t)nr, 0);
 }
 END_TEST
 
 START_TEST(findNodeInUA_NodeStoreWithSeveralEntries) {
     UA_Node* n1 = createNode(0,2253);
-    UA_NodeStore_insert(ns, n1);
+    ns.insertNode(ns.context, n1, NULL);
     UA_Node* n2 = createNode(0,2255);
-    UA_NodeStore_insert(ns, n2);
+    ns.insertNode(ns.context, n2, NULL);
     UA_Node* n3 = createNode(0,2257);
-    UA_NodeStore_insert(ns, n3);
+    ns.insertNode(ns.context, n3, NULL);
     UA_Node* n4 = createNode(0,2200);
-    UA_NodeStore_insert(ns, n4);
+    ns.insertNode(ns.context, n4, NULL);
     UA_Node* n5 = createNode(0,1);
-    UA_NodeStore_insert(ns, n5);
+    ns.insertNode(ns.context, n5, NULL);
     UA_Node* n6 = createNode(0,12);
-    UA_NodeStore_insert(ns, n6);
+    ns.insertNode(ns.context, n6, NULL);
 
     UA_NodeId in3 = UA_NODEID_NUMERIC(0, 2257);
-    const UA_Node* nr = UA_NodeStore_get(ns, &in3);
+    const UA_Node* nr = ns.getNode(ns.context, &in3);
     ck_assert_int_eq((uintptr_t)nr, (uintptr_t)n3);
+    ns.releaseNode(ns.context, nr);
 }
 END_TEST
 
 START_TEST(iterateOverUA_NodeStoreShallNotVisitEmptyNodes) {
     UA_Node* n1 = createNode(0,2253);
-    UA_NodeStore_insert(ns, n1);
+    ns.insertNode(ns.context, n1, NULL);
     UA_Node* n2 = createNode(0,2255);
-    UA_NodeStore_insert(ns, n2);
+    ns.insertNode(ns.context, n2, NULL);
     UA_Node* n3 = createNode(0,2257);
-    UA_NodeStore_insert(ns, n3);
+    ns.insertNode(ns.context, n3, NULL);
     UA_Node* n4 = createNode(0,2200);
-    UA_NodeStore_insert(ns, n4);
+    ns.insertNode(ns.context, n4, NULL);
     UA_Node* n5 = createNode(0,1);
-    UA_NodeStore_insert(ns, n5);
+    ns.insertNode(ns.context, n5, NULL);
     UA_Node* n6 = createNode(0,12);
-    UA_NodeStore_insert(ns, n6);
+    ns.insertNode(ns.context, n6, NULL);
 
     zeroCnt = 0;
     visitCnt = 0;
-    UA_NodeStore_iterate(ns,checkZeroVisitor);
+    ns.iterate(ns.context, NULL, checkZeroVisitor);
     ck_assert_int_eq(zeroCnt, 0);
     ck_assert_int_eq(visitCnt, 6);
 }
@@ -135,25 +156,26 @@ END_TEST
 START_TEST(findNodeInExpandedNamespace) {
     for(UA_UInt32 i = 0; i < 200; i++) {
         UA_Node* n = createNode(0,i);
-        UA_NodeStore_insert(ns, n);
+        ns.insertNode(ns.context, n, NULL);
     }
     // when
     UA_Node *n2 = createNode(0,25);
-    const UA_Node* nr = UA_NodeStore_get(ns,&n2->nodeId);
-    ck_assert_int_eq(nr->nodeId.identifier.numeric,n2->nodeId.identifier.numeric);
-    UA_NodeStore_deleteNode(n2);
+    const UA_Node* nr = ns.getNode(ns.context, &n2->nodeId);
+    ck_assert_int_eq(nr->nodeId.identifier.numeric, n2->nodeId.identifier.numeric);
+    ns.releaseNode(ns.context, nr);
+    ns.deleteNode(ns.context, n2);
 }
 END_TEST
 
 START_TEST(iterateOverExpandedNamespaceShallNotVisitEmptyNodes) {
     for(UA_UInt32 i = 0; i < 200; i++) {
-        UA_Node* n = createNode(0,i);
-        UA_NodeStore_insert(ns, n);
+        UA_Node* n = createNode(0,i+1);
+        ns.insertNode(ns.context, n, NULL);
     }
     // when
     zeroCnt = 0;
     visitCnt = 0;
-    UA_NodeStore_iterate(ns,checkZeroVisitor);
+    ns.iterate(ns.context, NULL, checkZeroVisitor);
     // then
     ck_assert_int_eq(zeroCnt, 0);
     ck_assert_int_eq(visitCnt, 200);
@@ -162,18 +184,18 @@ END_TEST
 
 START_TEST(failToFindNonExistantNodeInUA_NodeStoreWithSeveralEntries) {
     UA_Node* n1 = createNode(0,2253);
-    UA_NodeStore_insert(ns, n1);
+    ns.insertNode(ns.context, n1, NULL);
     UA_Node* n2 = createNode(0,2255);
-    UA_NodeStore_insert(ns, n2);
+    ns.insertNode(ns.context, n2, NULL);
     UA_Node* n3 = createNode(0,2257);
-    UA_NodeStore_insert(ns, n3);
+    ns.insertNode(ns.context, n3, NULL);
     UA_Node* n4 = createNode(0,2200);
-    UA_NodeStore_insert(ns, n4);
+    ns.insertNode(ns.context, n4, NULL);
     UA_Node* n5 = createNode(0,1);
-    UA_NodeStore_insert(ns, n5);
+    ns.insertNode(ns.context, n5, NULL);
 
     UA_NodeId id = UA_NODEID_NUMERIC(0, 12);
-    const UA_Node* nr = UA_NodeStore_get(ns, &id);
+    const UA_Node* nr = ns.getNode(ns.context, &id);
     ck_assert_int_eq((uintptr_t)nr, 0);
 }
 END_TEST
@@ -190,32 +212,32 @@ struct UA_NodeStoreProfileTest {
 };
 
 static void *profileGetThread(void *arg) {
-    rcu_register_thread();
-    UA_RCU_LOCK();
     struct UA_NodeStoreProfileTest *test = (struct UA_NodeStoreProfileTest*) arg;
     UA_NodeId id;
     UA_NodeId_init(&id);
     UA_Int32 max_val = test->max_val;
     for(UA_Int32 x = 0; x<test->rounds; x++) {
         for(UA_Int32 i=test->min_val; i<max_val; i++) {
-            id.identifier.numeric = i;
-            UA_NodeStore_get(ns,&id);
+            id.identifier.numeric = i+1;
+            const UA_Node *n = ns.getNode(ns.context, &id);
+            ns.releaseNode(ns.context, n);
         }
     }
-    UA_RCU_UNLOCK();
-    rcu_unregister_thread();
     return NULL;
 }
 #endif
 
+#define N 1000 /* make bigger to test */
+
 START_TEST(profileGetDelete) {
-#define N 100 /* make bigger to test */
-    for(UA_UInt32 i = 0; i < N; i++) {
-        UA_Node *n = createNode(0,i);
-        UA_NodeStore_insert(ns, n);
-    }
     clock_t begin, end;
     begin = clock();
+
+    for(UA_UInt32 i = 0; i < N; i++) {
+        UA_Node *n = createNode(0,i+1);
+        ns.insertNode(ns.context, n, NULL);
+    }
+
 #ifdef UA_ENABLE_MULTITHREADING
 #define THREADS 4
     pthread_t t[THREADS];
@@ -229,16 +251,15 @@ START_TEST(profileGetDelete) {
     end = clock();
     printf("Time for %d create/get/delete on %d threads in a namespace: %fs.\n", N, THREADS, (double)(end - begin) / CLOCKS_PER_SEC);
 #else
-    UA_NodeId id;
-    UA_NodeId_init(&id);
-    for(UA_Int32 x = 0; x<50; x++) {
-        for(int i=0; i<N; i++) {
-            id.identifier.numeric = i;
-            UA_NodeStore_get(ns,&id);
-        }
+    UA_NodeId id = UA_NODEID_NULL;
+    for(size_t i = 0; i < N; i++) {
+        id.identifier.numeric = (UA_UInt32)i+1;
+        const UA_Node *node = ns.getNode(ns.context, &id);
+        ns.releaseNode(ns.context, node);
     }
     end = clock();
-    printf("Time for single-threaded %d create/get/delete in a namespace: %fs.\n", N, (double)(end - begin) / CLOCKS_PER_SEC);
+    printf("Time for single-threaded %d create/get/delete in a namespace: %fs.\n", N,
+           (double)(end - begin) / CLOCKS_PER_SEC);
 #endif
 }
 END_TEST
@@ -277,10 +298,6 @@ static Suite * namespace_suite (void) {
 
 
 int main (void) {
-#ifdef UA_ENABLE_MULTITHREADING
-    rcu_init();
-    rcu_register_thread();
-#endif
     int number_failed = 0;
     Suite *s = namespace_suite();
     SRunner *sr = srunner_create(s);
@@ -288,9 +305,5 @@ int main (void) {
     srunner_run_all(sr, CK_NORMAL);
     number_failed += srunner_ntests_failed (sr);
     srunner_free(sr);
-#ifdef UA_ENABLE_MULTITHREADING
-    rcu_barrier();
-    rcu_unregister_thread();
-#endif
     return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
 }

+ 4 - 4
tests/check_server_readspeed.c

@@ -26,9 +26,10 @@ int main(int argc, char** argv) {
     UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME(1, "the answer");
     UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
     UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
-    UA_Server_addVariableNode(server, myIntegerNodeId, parentNodeId,
-                              parentReferenceNodeId, myIntegerName,
-                              UA_NODEID_NULL, attr, NULL, NULL);
+    UA_StatusCode retval = UA_Server_addVariableNode(server, myIntegerNodeId, parentNodeId,
+                                                     parentReferenceNodeId, myIntegerName,
+                                                     UA_NODEID_NULL, attr, NULL, NULL);
+    assert(retval == UA_STATUSCODE_GOOD);
 
     UA_ReadRequest request;
     UA_ReadRequest_init(&request);
@@ -41,7 +42,6 @@ int main(int argc, char** argv) {
     request.nodesToReadSize = 1;
     request.nodesToRead = &rvi;
 
-    UA_StatusCode retval = UA_STATUSCODE_GOOD;
     UA_ByteString request_msg;
     retval |= UA_ByteString_allocBuffer(&request_msg, 1000);
     UA_ByteString response_msg;

+ 6 - 7
tests/check_services_attributes.c

@@ -7,7 +7,6 @@
 #include <time.h>
 
 #include "check.h"
-#include "server/ua_nodestore.h"
 #include "server/ua_services.h"
 #include "ua_client.h"
 #include "ua_types.h"
@@ -134,7 +133,9 @@ static void setup(void) {
 }
 
 static UA_VariableNode* makeCompareSequence(void) {
-    UA_VariableNode *node = UA_NodeStore_newVariableNode();
+    UA_VariableNode *node = (UA_VariableNode*)
+        config->nodestore.newNode(config->nodestore.context,
+                                  UA_NODECLASS_VARIABLE);
 
     UA_Int32 myInteger = 42;
     UA_Variant_setScalarCopy(&node->value.data.value.value, &myInteger, &UA_TYPES[UA_TYPES_INT32]);
@@ -246,7 +247,7 @@ START_TEST(ReadSingleAttributeDisplayNameWithoutTimestamp) {
     ck_assert(UA_String_equal(&comp.text, &respval->text));
     ck_assert(UA_String_equal(&compNode->displayName.locale, &respval->locale));
     UA_DataValue_deleteMembers(&resp);
-    UA_NodeStore_deleteNode((UA_Node*)compNode);
+    config->nodestore.deleteNode(config->nodestore.context, (UA_Node*)compNode);
 } END_TEST
 
 START_TEST(ReadSingleAttributeDescriptionWithoutTimestamp) {
@@ -264,7 +265,7 @@ START_TEST(ReadSingleAttributeDescriptionWithoutTimestamp) {
     ck_assert(UA_String_equal(&compNode->description.locale, &respval->locale));
     ck_assert(UA_String_equal(&compNode->description.text, &respval->text));
     UA_DataValue_deleteMembers(&resp);
-    UA_NodeStore_deleteNode((UA_Node*)compNode);
+    config->nodestore.deleteNode(config->nodestore.context, (UA_Node*)compNode);
 } END_TEST
 
 START_TEST(ReadSingleAttributeWriteMaskWithoutTimestamp) {
@@ -441,13 +442,11 @@ START_TEST(ReadSingleAttributeUserAccessLevelWithoutTimestamp) {
     UA_DataValue resp = UA_Server_read(server, &rvi, UA_TIMESTAMPSTORETURN_NEITHER);
 
     /* Uncommented since the accesslevel is always 0xff for the local admin user */
-    /* UA_RCU_LOCK(); */
     /* const UA_VariableNode* compNode = */
     /*     (const UA_VariableNode*)UA_NodeStore_get(server->nodestore, &rvi.nodeId); */
     /* ck_assert_int_eq(0, resp.value.arrayLength); */
     /* ck_assert_ptr_eq(&UA_TYPES[UA_TYPES_BYTE], resp.value.type); */
     /* ck_assert_int_eq(*(UA_Byte*)resp.value.data, compNode->accessLevel & 0xFF); // 0xFF is the default userAccessLevel */
-    /* UA_RCU_UNLOCK(); */
     UA_DataValue_deleteMembers(&resp);
 } END_TEST
 
@@ -466,7 +465,7 @@ START_TEST(ReadSingleAttributeMinimumSamplingIntervalWithoutTimestamp) {
     ck_assert_ptr_eq(&UA_TYPES[UA_TYPES_DOUBLE], resp.value.type);
     ck_assert(*respval == comp);
     UA_DataValue_deleteMembers(&resp);
-    UA_NodeStore_deleteNode((UA_Node*)compNode);
+    config->nodestore.deleteNode(config->nodestore.context, (UA_Node*)compNode);
 } END_TEST
 
 START_TEST(ReadSingleAttributeHistorizingWithoutTimestamp) {

+ 0 - 6
tests/check_services_nodemanagement.c

@@ -7,18 +7,12 @@
 #include <time.h>
 
 #include "check.h"
-#include "server/ua_nodestore.h"
 #include "server/ua_services.h"
 #include "ua_client.h"
 #include "ua_types.h"
 #include "ua_config_default.h"
 #include "server/ua_server_internal.h"
 
-#ifdef UA_ENABLE_MULTITHREADING
-#include <pthread.h>
-#include <urcu.h>
-#endif
-
 static UA_Server *server = NULL;
 static UA_ServerConfig *config = NULL;
 static UA_Int32 handleCalled = 0;

+ 1 - 0
tests/fuzz/CMakeLists.txt

@@ -38,6 +38,7 @@ set(fuzzing_plugin_sources ${PROJECT_SOURCE_DIR}/plugins/ua_network_tcp.c
         ${PROJECT_SOURCE_DIR}/tests/testing_networklayers.c
         ${PROJECT_SOURCE_DIR}/plugins/ua_log_stdout.c
         ${PROJECT_SOURCE_DIR}/plugins/ua_config_default.c
+        ${PROJECT_SOURCE_DIR}/plugins/ua_nodestore_default.c
         ${PROJECT_SOURCE_DIR}/plugins/ua_accesscontrol_default.c)
 
 add_library(open62541-fuzzplugins OBJECT ${fuzzing_plugin_sources})

+ 1 - 71
tools/pyUANamespace/open62541_MacroHelper.py

@@ -199,8 +199,7 @@ class open62541_MacroHelper():
                 argumentCnt += 1
 
     # print the attributes struct
-    code.append("UA_%sAttributes attr;" % nodetype)
-    code.append("UA_%sAttributes_init(&attr);" %  nodetype);
+    code.append("UA_%sAttributes attr = UA_%sAttributes_default;" % (nodetype, nodetype))
     code.append("attr.displayName = UA_LOCALIZEDTEXT(\"\", \"" + str(node.displayName()) + "\");")
     code.append("attr.description = UA_LOCALIZEDTEXT(\"\", \"" + str(node.description()) + "\");")
     
@@ -256,72 +255,3 @@ class open62541_MacroHelper():
     if nodetype is "Variable":
       code.append("UA_Server_deleteReference(server, nodeId, UA_NODEID_NUMERIC(0, 40), true, UA_EXPANDEDNODEID_NUMERIC(0, 62), true); //remove HasTypeDefinition refs generated by addVariableNode");
     return code
-
-  def getCreateNodeBootstrap(self, node):
-    nodetype = ""
-    code = []
-
-    code.append("// Node: " + str(node) + ", " + str(node.browseName()))
-
-    if node.nodeClass() == NODE_CLASS_OBJECT:
-      nodetype = "Object"
-    elif node.nodeClass() == NODE_CLASS_VARIABLE:
-      nodetype = "Variable"
-    elif node.nodeClass() == NODE_CLASS_METHOD:
-      nodetype = "Method"
-    elif node.nodeClass() == NODE_CLASS_OBJECTTYPE:
-      nodetype = "ObjectType"
-    elif node.nodeClass() == NODE_CLASS_REFERENCETYPE:
-      nodetype = "ReferenceType"
-    elif node.nodeClass() == NODE_CLASS_VARIABLETYPE:
-      nodetype = "VariableType"
-    elif node.nodeClass() == NODE_CLASS_DATATYPE:
-      nodetype = "DataType"
-    elif node.nodeClass() == NODE_CLASS_VIEW:
-      nodetype = "View"
-    else:
-      code.append("/* undefined nodeclass */")
-      return;
-
-    code.append("UA_" + nodetype + "Node *" + node.getCodePrintableID() + " = UA_NodeStore_new" + nodetype + "Node();")
-    if not "browsename" in self.supressGenerationOfAttribute:
-      extrNs = node.browseName().split(":")
-      if len(extrNs) > 1:
-        code.append(node.getCodePrintableID() + "->browseName = UA_QUALIFIEDNAME_ALLOC(" +  str(extrNs[0]) + ", \"" + extrNs[1] + "\");")
-      else:
-        code.append(node.getCodePrintableID() + "->browseName = UA_QUALIFIEDNAME_ALLOC(0, \"" + node.browseName() + "\");")
-    if not "displayname" in self.supressGenerationOfAttribute:
-      code.append(node.getCodePrintableID() + "->displayName = UA_LOCALIZEDTEXT_ALLOC(\"\", \"" +  node.displayName() + "\");")
-    if not "description" in self.supressGenerationOfAttribute:
-      code.append(node.getCodePrintableID() + "->description = UA_LOCALIZEDTEXT_ALLOC(\"\", \"" +  node.description() + "\");")
-
-    if not "writemask" in self.supressGenerationOfAttribute:
-        if node.__node_writeMask__ != 0:
-          code.append(node.getCodePrintableID() + "->writeMask = (UA_Int32) " +  str(node.__node_writeMask__) + ";")
-    if not "userwritemask" in self.supressGenerationOfAttribute:
-        if node.__node_userWriteMask__ != 0:
-          code.append(node.getCodePrintableID() + "->userWriteMask = (UA_Int32) " + str(node.__node_userWriteMask__) + ";")
-    if not "nodeid" in self.supressGenerationOfAttribute:
-      if node.id().ns != 0:
-        code.append(node.getCodePrintableID() + "->nodeId.namespaceIndex = " + str(node.id().ns) + ";")
-      if node.id().i != None:
-        code.append(node.getCodePrintableID() + "->nodeId.identifier.numeric = " + str(node.id().i) + ";")
-      elif node.id().b != None:
-        code.append(node.getCodePrintableID() + "->nodeId.identifierType = UA_NODEIDTYPE_BYTESTRING;")
-        logger.error("ByteString IDs for nodes has not been implemented yet.")
-        return []
-      elif node.id().g != None:
-        #<jpfr> the string is sth like { .length = 111, .data = <ptr> }
-        #<jpfr> there you _may_ alloc the <ptr> on the heap
-        #<jpfr> for the guid, just set it to {.data1 = 111, .data2 = 2222, ....
-        code.append(node.getCodePrintableID() + "->nodeId.identifierType = UA_NODEIDTYPE_GUID;")
-        logger.error(self, "GUIDs for nodes has not been implemented yet.")
-        return []
-      elif node.id().s != None:
-        code.append(node.getCodePrintableID() + "->nodeId.identifierType = UA_NODEIDTYPE_STRING;")
-        code.append(node.getCodePrintableID() + "->nodeId.identifier.numeric = UA_STRING_ALLOC(\"" + str(node.id().i) + "\");")
-      else:
-        logger.error("Node ID is not numeric, bytestring, guid or string. I do not know how to create c code for that...")
-        return []
-
-    return code

+ 3 - 14
tools/pyUANamespace/ua_namespace.py

@@ -647,12 +647,10 @@ class opcua_namespace():
     header.append('#ifndef '+outfilename.upper()+'_H_')
     header.append('#define '+outfilename.upper()+'_H_')
     header.append('')
-    header.append('#include "server/ua_server_internal.h"')
-    header.append('#include "server/ua_nodes.h"')
-    header.append('#include "ua_util.h"')
+    header.append('#include "ua_server.h"')
+    header.append('#include "ua_plugin_nodestore.h"')
     header.append('#include "ua_types_encoding_binary.h"')
     header.append('#include "ua_types_generated_encoding_binary.h"')
-    header.append('#include "ua_transport_generated_encoding_binary.h"')
     header.append('')
     header.append('/* Definition that (in userspace models) may be ')
     header.append(' * - not included in the amalgamated header or')
@@ -663,18 +661,9 @@ class opcua_namespace():
     header.append('#ifndef UA_ENCODINGOFFSET_BINARY')
     header.append('#  define UA_ENCODINGOFFSET_BINARY 2')
     header.append('#endif')
-    header.append('#ifndef NULL')
-    header.append('  #define NULL ((void *)0)')
-    header.append('#endif')
-    header.append('#ifndef UA_malloc')
-    header.append('  #define UA_malloc(_p_size) malloc(_p_size)')
-    header.append('#endif')
-    header.append('#ifndef UA_free')
-    header.append('  #define UA_free(_p_ptr) free(_p_ptr)')
-    header.append('#endif')
     
     code.append('#include "'+outfilename+'.h"')
-    code.append("UA_INLINE UA_StatusCode "+outfilename+"(UA_Server *server) {")
+    code.append("UA_INLINE UA_StatusCode "+outfilename+"(UA_Server *server) {\n")
     code.append('UA_StatusCode retval = UA_STATUSCODE_GOOD; ')
     code.append('if(retval == UA_STATUSCODE_GOOD){retval = UA_STATUSCODE_GOOD;} //ensure that retval is used');
 

+ 1 - 7
tools/pyUANamespace/ua_node_types.py

@@ -693,13 +693,7 @@ class opcua_node_t:
             unPrintedReferences.remove(ref)
     # Otherwise use the "Bootstrapping" method and we will get registered with other nodes later.
     else:
-      code = code + self.printOpen62541CCode_SubtypeEarly(bootstrapping = True)
-      code = code + codegen.getCreateNodeBootstrap(self)
-      code = code + self.printOpen62541CCode_Subtype(unPrintedReferences = unPrintedReferences, bootstrapping = True)
-      code.append("// Parent node does not exist yet. This node will be bootstrapped and linked later.")
-      code.append("UA_RCU_LOCK();")
-      code.append("UA_NodeStore_insert(server->nodestore, (UA_Node*) " + self.getCodePrintableID() + ");")
-      code.append("UA_RCU_UNLOCK();")
+      code.append("/* Omit bootstrapping the node %s */" % str(self.id()))
 
     # Try to print all references to nodes that already exist
     # Note: we know the reference types exist, because the namespace class made sure they were