Browse Source

remove the old copyNode function; refactor the write service

Julius Pfrommer 9 years ago
parent
commit
010c2d90ab

+ 2 - 5
examples/server.c

@@ -232,15 +232,12 @@ int main(int argc, char** argv) {
                                   dateDataSource, &nodeId_currentTime);
 
   // Get and reattach the datasource
-  UA_DataSource *dataSourceCopy = NULL;
+  UA_DataSource dataSourceCopy;
   UA_Server_getNodeAttribute_valueDataSource(server, nodeId_currentTime, &dataSourceCopy);
-  if (dataSourceCopy == NULL)
-    UA_LOG_WARNING(logger, UA_LOGCATEGORY_USERLAND, "The returned dataSource is invalid");
-  else if (dataSourceCopy->read != dateDataSource.read)
+  if (dataSourceCopy.read != dateDataSource.read)
     UA_LOG_WARNING(logger, UA_LOGCATEGORY_USERLAND, "The returned dataSource is not the same as we set?");
   else
     UA_Server_setNodeAttribute_valueDataSource(server, nodeId_currentTime, dataSourceCopy);
-  free(dataSourceCopy);
 #ifndef _WIN32
   //cpu temperature monitoring for linux machines
   if((temperatureFile = fopen("/sys/class/thermal/thermal_zone0/temp", "r"))) {

+ 2 - 5
include/ua_server.h

@@ -135,9 +135,6 @@ UA_StatusCode UA_EXPORT UA_Server_addReference(UA_Server *server, const UA_NodeI
  */
 UA_StatusCode UA_EXPORT UA_Server_deleteNode(UA_Server *server, UA_NodeId nodeId);
 
-UA_StatusCode UA_Server_getNodeCopy(UA_Server *server, UA_NodeId nodeId, void **copyInto);
-UA_StatusCode UA_Server_deleteNodeCopy(UA_Server *server, void **nodeptr);
-
 /** A new variable Node with a value passed in variant.
  *
  * @param server The server object
@@ -323,7 +320,7 @@ UA_Server_setNodeAttribute_method(UA_Server *server, UA_NodeId methodNodeId,
 #endif
 
 UA_StatusCode UA_EXPORT
-UA_Server_setNodeAttribute_valueDataSource(UA_Server *server, UA_NodeId nodeId, UA_DataSource *value);
+UA_Server_setNodeAttribute_valueDataSource(UA_Server *server, UA_NodeId nodeId, UA_DataSource dataSource);
 
 /***********************/
 /* Get Node Attributes */
@@ -398,7 +395,7 @@ UA_Server_getNodeAttribute_method(UA_Server *server, UA_NodeId methodNodeId, UA_
 #endif
 
 UA_StatusCode UA_EXPORT
-UA_Server_getNodeAttribute_valueDataSource(UA_Server *server, UA_NodeId nodeId, UA_DataSource **value);
+UA_Server_getNodeAttribute_valueDataSource(UA_Server *server, UA_NodeId nodeId, UA_DataSource *dataSource);
 
 /** Jobs describe work that is executed once or repeatedly. */
 typedef struct {

+ 59 - 455
src/server/ua_server_addressspace.c

@@ -2,97 +2,6 @@
 #include "ua_services.h"
 #include "ua_server_internal.h"
 
-UA_StatusCode UA_Server_getNodeCopy(UA_Server *server, UA_NodeId nodeId, void **copyInto) {
-  const UA_Node *node = UA_NodeStore_get(server->nodestore, &nodeId);
-  UA_Node **copy = (UA_Node **) copyInto;
-  
-  UA_StatusCode retval = UA_STATUSCODE_GOOD;
-  
-  if (!node)
-    return UA_STATUSCODE_BADNODEIDINVALID;
-  
-  switch(node->nodeClass) {
-    case UA_NODECLASS_DATATYPE:
-      *copy = (UA_Node *) UA_VariableNode_new();
-      retval |= UA_DataTypeNode_copy((const UA_DataTypeNode *) node, (UA_DataTypeNode *) *copy);
-      break;
-    case UA_NODECLASS_METHOD:
-      *copy =  (UA_Node *) UA_MethodNode_new();
-      retval |= UA_MethodNode_copy((const UA_MethodNode *) node, (UA_MethodNode *) *copy);
-      break;      
-    case UA_NODECLASS_OBJECT:
-      *copy =  (UA_Node *) UA_ObjectNode_new();
-      retval |= UA_ObjectNode_copy((const UA_ObjectNode *) node, (UA_ObjectNode *) *copy);
-      break;      
-    case UA_NODECLASS_OBJECTTYPE:
-      *copy =  (UA_Node *) UA_ObjectTypeNode_new();
-      retval |= UA_ObjectTypeNode_copy((const UA_ObjectTypeNode *) node, (UA_ObjectTypeNode *) *copy);
-      break;      
-    case UA_NODECLASS_REFERENCETYPE:
-      *copy =  (UA_Node *) UA_ReferenceTypeNode_new();
-      retval |= UA_ReferenceTypeNode_copy((const UA_ReferenceTypeNode *) node, (UA_ReferenceTypeNode *) *copy);
-      break;      
-    case UA_NODECLASS_VARIABLE:
-      *copy =  (UA_Node *) UA_VariableNode_new();
-      retval |= UA_VariableNode_copy((const UA_VariableNode *) node, (UA_VariableNode *) *copy);
-      break;      
-    case UA_NODECLASS_VARIABLETYPE:
-      *copy =  (UA_Node *) UA_VariableTypeNode_new();
-      retval |= UA_VariableTypeNode_copy((const UA_VariableTypeNode *) node, (UA_VariableTypeNode *) *copy);
-      break;      
-    case UA_NODECLASS_VIEW:
-      *copy =  (UA_Node *) UA_ViewNode_new();
-      retval |= UA_ViewNode_copy((const UA_ViewNode *) node, (UA_ViewNode *) *copy);
-      break;      
-    default:
-      break;
-  }
-  
-  UA_NodeStore_release(node);
-  
-  return retval;
-} 
-
-UA_StatusCode UA_Server_deleteNodeCopy(UA_Server *server, void **nodeptr) {
-  UA_StatusCode retval = UA_STATUSCODE_GOOD;
-  UA_Node **node = (UA_Node **) nodeptr;
-  
-  if (!(*node))
-    return UA_STATUSCODE_BADNODEIDINVALID;
-  
-  switch((*node)->nodeClass) {
-    case UA_NODECLASS_DATATYPE:
-      UA_DataTypeNode_delete((UA_DataTypeNode *) *node);
-      break;
-    case UA_NODECLASS_METHOD:
-      UA_MethodNode_delete((UA_MethodNode *) *node);
-      break;      
-    case UA_NODECLASS_OBJECT:
-      UA_ObjectNode_delete((UA_ObjectNode *) *node);
-      break;      
-    case UA_NODECLASS_OBJECTTYPE:
-      UA_ObjectTypeNode_delete((UA_ObjectTypeNode *) *node);
-      break;      
-    case UA_NODECLASS_REFERENCETYPE:
-      UA_ReferenceTypeNode_delete((UA_ReferenceTypeNode *) *node);
-      break;      
-    case UA_NODECLASS_VARIABLE:
-      UA_VariableNode_delete((UA_VariableNode *) *node);
-      break;      
-    case UA_NODECLASS_VARIABLETYPE:
-      UA_VariableTypeNode_delete((UA_VariableTypeNode *) *node);
-      break;      
-    case UA_NODECLASS_VIEW:
-      UA_ViewNode_delete((UA_ViewNode *) *node);
-      break;      
-    default:
-      break;
-  }
-  
-  return retval;
-} 
-
-
 UA_StatusCode UA_Server_deleteNode(UA_Server *server, UA_NodeId nodeId) {
   union nodeUnion {
     const UA_Node *delNodeConst;
@@ -893,51 +802,50 @@ UA_Server_setNodeAttribute_method(UA_Server *server, UA_NodeId methodNodeId, UA_
 #endif
 
 UA_StatusCode
-UA_Server_setNodeAttribute_valueDataSource(UA_Server *server, UA_NodeId nodeId, UA_DataSource *value) {
-  union {
-    UA_Node *anyNode;
-    UA_VariableNode *varNode;
-    UA_VariableTypeNode *varTNode;
-  } node;
-  UA_StatusCode retval;
-  retval = UA_Server_getNodeCopy(server, nodeId, (void **) &node.anyNode);
-  
-  if (retval != UA_STATUSCODE_GOOD || node.anyNode == UA_NULL)
-    return retval;
-  
-  if (node.anyNode->nodeClass != UA_NODECLASS_VARIABLE && node.anyNode->nodeClass != UA_NODECLASS_VARIABLETYPE) {
-    UA_Server_deleteNodeCopy(server, (void **) &node);
-    return UA_STATUSCODE_BADNODECLASSINVALID;
-  }
-  
-  if (node.anyNode->nodeClass == UA_NODECLASS_VARIABLE) {
-    if (node.varNode->valueSource == UA_VALUESOURCE_VARIANT) {
-      UA_Variant_deleteMembers(&node.varNode->value.variant);
+UA_Server_setNodeAttribute_valueDataSource(UA_Server *server, const UA_NodeId nodeId, UA_DataSource dataSource) {
+    const UA_Node *orig;
+ retrySetDataSource:
+    orig = UA_NodeStore_get(server->nodestore, &nodeId);
+    if(!orig)
+        return UA_STATUSCODE_BADNODEIDUNKNOWN;
+
+    if(orig->nodeClass != UA_NODECLASS_VARIABLE &&
+       orig->nodeClass != UA_NODECLASS_VARIABLETYPE) {
+        UA_NodeStore_release(orig);
+        return UA_STATUSCODE_BADNODECLASSINVALID;
     }
-    node.varNode->valueSource = UA_VALUESOURCE_DATASOURCE;
-    node.varNode->value.dataSource.handle = value->handle;
-    node.varNode->value.dataSource.read   = value->read;
-    node.varNode->value.dataSource.write  = value->write;
-  }
-  else {
-    if (node.varTNode->valueSource == UA_VALUESOURCE_VARIANT) {
-      UA_Variant_deleteMembers(&node.varTNode->value.variant);
+    
+#ifndef UA_MULTITHREADING
+    /* We cheat if multithreading is not enabled and treat the node as mutable. */
+    UA_VariableNode *editable = (UA_VariableNode*)(uintptr_t)orig;
+#else
+    UA_VariableNode *editable = (UA_VariableNode*)UA_Node_copyAnyNodeClass(orig);
+    if(!editable) {
+        UA_NodeStore_release(orig);
+        return UA_STATUSCODE_BADOUTOFMEMORY;
     }
-    node.varTNode->valueSource = UA_VALUESOURCE_DATASOURCE;
-    node.varTNode->value.dataSource.handle = value->handle;
-    node.varTNode->value.dataSource.read   = value->read;
-    node.varTNode->value.dataSource.write  = value->write;
-  }
+#endif
+
+    if(editable->valueSource == UA_VALUESOURCE_VARIANT)
+        UA_Variant_deleteMembers(&editable->value.variant);
+    editable->value.dataSource = dataSource;
+    editable->valueSource = UA_VALUESOURCE_DATASOURCE;
   
-  const UA_Node **inserted = UA_NULL;
-  const UA_Node *oldNode = UA_NodeStore_get(server->nodestore, &node.anyNode->nodeId);
-  retval |= UA_NodeStore_replace(server->nodestore, oldNode, node.anyNode, inserted);
-  UA_NodeStore_release(oldNode);
- 
-  return retval;
+#ifdef UA_MULTITHREADING
+    UA_StatusCode retval = UA_NodeStore_replace(server->nodestore, orig, (UA_Node*)editable, UA_NULL);
+    if(retval != UA_STATUSCODE_GOOD) {
+        /* The node was replaced in the background */
+        UA_NodeStore_release(orig);
+        goto retrySetDataSource;
+    }
+#endif
+    UA_NodeStore_release(orig);
+    return UA_STATUSCODE_GOOD;
 }
 
-UA_StatusCode UA_Server_getNodeAttribute(UA_Server *server, const UA_NodeId nodeId, UA_AttributeId attributeId, UA_Variant *v) {
+UA_StatusCode
+UA_Server_getNodeAttribute(UA_Server *server, const UA_NodeId nodeId,
+                           UA_AttributeId attributeId, UA_Variant *v) {
     const UA_ReadValueId rvi = {.nodeId = nodeId, .attributeId = attributeId, .indexRange = UA_STRING_NULL,
                                 .dataEncoding = UA_QUALIFIEDNAME(0, "DefaultBinary")};
     UA_DataValue dv;
@@ -967,7 +875,8 @@ UA_StatusCode UA_Server_getNodeAttributeUnpacked(UA_Server *server, const UA_Nod
 }
 
 #ifdef ENABLE_METHODCALLS
-UA_StatusCode UA_Server_getNodeAttribute_method(UA_Server *server, UA_NodeId nodeId, UA_MethodCallback *method) {
+UA_StatusCode
+UA_Server_getNodeAttribute_method(UA_Server *server, UA_NodeId nodeId, UA_MethodCallback *method) {
     const UA_Node *node = UA_NodeStore_get(server->nodestore, &nodeId);
     if(!node)
         return UA_STATUSCODE_BADNODEIDUNKNOWN;
@@ -983,47 +892,26 @@ UA_StatusCode UA_Server_getNodeAttribute_method(UA_Server *server, UA_NodeId nod
 }
 #endif
 
-UA_StatusCode UA_Server_getNodeAttribute_valueDataSource(UA_Server *server, UA_NodeId nodeId, UA_DataSource **value) {
-  union {
-    UA_Node *anyNode;
-    UA_VariableNode *varNode;
-    UA_VariableTypeNode *varTNode;
-  } node;
-  UA_StatusCode retval;
-  *value = UA_NULL;
-  
-  retval = UA_Server_getNodeCopy(server, nodeId, (void **) &node.anyNode);
-  if (retval != UA_STATUSCODE_GOOD || node.anyNode == UA_NULL)
-    return retval;
-  
-  if (node.anyNode->nodeClass != UA_NODECLASS_VARIABLE && node.anyNode->nodeClass != UA_NODECLASS_VARIABLETYPE) {
-    UA_Server_deleteNodeCopy(server, (void **) &node);
-    return UA_STATUSCODE_BADNODECLASSINVALID;
-  }
-  
-  if (node.anyNode->nodeClass == UA_NODECLASS_VARIABLE) {
-    if (node.varNode->valueSource == UA_VALUESOURCE_VARIANT) {
-      retval |= UA_Server_deleteNodeCopy(server, (void **) &node);
-      return UA_STATUSCODE_BADINVALIDARGUMENT;
+UA_StatusCode
+UA_Server_getNodeAttribute_valueDataSource(UA_Server *server, UA_NodeId nodeId, UA_DataSource *dataSource) {
+    const UA_VariableNode *node = (const UA_VariableNode*)UA_NodeStore_get(server->nodestore, &nodeId);
+    if(!node)
+        return UA_STATUSCODE_BADNODEIDUNKNOWN;
+
+    if(node->nodeClass != UA_NODECLASS_VARIABLE &&
+       node->nodeClass != UA_NODECLASS_VARIABLETYPE) {
+        UA_NodeStore_release((const UA_Node*)node);
+        return UA_STATUSCODE_BADNODECLASSINVALID;
     }
-    *value = (UA_DataSource *) UA_malloc(sizeof(UA_DataSource));
-    (*(value))->handle = node.varNode->value.dataSource.handle;
-    (*(value))->read   = node.varNode->value.dataSource.read;
-    (*(value))->write  = node.varNode->value.dataSource.write;
-  }
-  else {
-    if (node.varTNode->valueSource == UA_VALUESOURCE_VARIANT) {
-      retval |= UA_Server_deleteNodeCopy(server, (void **) &node);
-      return UA_STATUSCODE_BADINVALIDARGUMENT;
+
+    if(node->valueSource != UA_VALUESOURCE_DATASOURCE) {
+        UA_NodeStore_release((const UA_Node*)node);
+        return UA_STATUSCODE_BADNODECLASSINVALID;
     }
-    *value = (UA_DataSource *) UA_malloc(sizeof(UA_DataSource));
-    (*(value))->handle = node.varNode->value.dataSource.handle;
-    (*(value))->read   = node.varNode->value.dataSource.read;
-    (*(value))->write  = node.varNode->value.dataSource.write;
-  }
-  
-  retval |= UA_Server_deleteNodeCopy(server, (void **) &node);
-  return retval;
+
+    *dataSource = node->value.dataSource;
+    UA_NodeStore_release((const UA_Node*)node);
+    return UA_STATUSCODE_GOOD;
 }
 
 #define arrayOfNodeIds_addNodeId(ARRAYNAME, NODEID) { \
@@ -1046,288 +934,4 @@ UA_StatusCode UA_Server_getNodeAttribute_valueDataSource(UA_Server *server, UA_N
       break; \
     } \
   } \
-} \
-
-static void UA_Server_addInstanceOf_inheritParentAttributes(UA_Server *server, arrayOfNodeIds *subtypeRefs, arrayOfNodeIds *componentRefs, 
-                                                            UA_NodeId objectRoot, UA_InstantiationCallback callback, UA_ObjectTypeNode *typeDefNode,
-                                                            arrayOfNodeIds *instantiatedTypes, void *handle) 
-{
-  UA_Boolean refTypeValid;
-  UA_ReferenceNode ref;
-  arrayOfNodeIds visitedNodes = (arrayOfNodeIds) {.size=0, .ids = UA_NULL};
-  for(int i=0; i<typeDefNode->referencesSize; i++) {
-    ref = typeDefNode->references[i];
-    if (ref.isInverse == UA_FALSE)
-      continue;
-    refTypeValid = UA_FALSE;
-    arrayOfNodeIds_idInArray((*subtypeRefs), ref.referenceTypeId, refTypeValid, UA_FALSE);
-    if (!refTypeValid) 
-      continue;
-    
-    // Check if already tried to inherit from this node (there is such a thing as duplicate refs)
-    arrayOfNodeIds_idInArray(visitedNodes, ref.targetId.nodeId, refTypeValid, UA_TRUE);
-    if (!refTypeValid) 
-      continue;
-    // Go ahead and inherit this nodes variables and methods (not objects!)
-    arrayOfNodeIds_addNodeId(visitedNodes, ref.targetId.nodeId);
-    UA_Server_appendInstanceOfSupertype(server, ref.targetId.nodeId, objectRoot, subtypeRefs, componentRefs, callback, instantiatedTypes, handle);
-  } // End check all hassubtype refs
-  arrayOfNodeIds_deleteMembers(visitedNodes);
-  return;
 }
-
-static void UA_Server_addInstanceOf_instatiateChildObject(UA_Server *server, arrayOfNodeIds *subtypeRefs,
-                                                          arrayOfNodeIds *componentRefs, arrayOfNodeIds *typedefRefs,
-                                                          UA_Node *objectCopy, UA_NodeId parentId, UA_ExpandedNodeId typeDefinition,
-                                                          UA_NodeId referenceTypeId, UA_InstantiationCallback callback,
-                                                          UA_Boolean instantiateObjects, arrayOfNodeIds *instantiatedTypes, 
-                                                          void *handle) {
-  UA_StatusCode retval = UA_STATUSCODE_GOOD;
-  
-  // Refuse to create this node if we detect a circular type definition
-  UA_Boolean typeDefRecursion;
-  arrayOfNodeIds_idInArray((*instantiatedTypes), typeDefinition.nodeId, typeDefRecursion, UA_FALSE);
-  if (typeDefRecursion) 
-    return;
-  
-  UA_Node *typeDefNode;
-  UA_Server_getNodeCopy(server, typeDefinition.nodeId, (void *) &typeDefNode);
-  
-  if (typeDefNode == UA_NULL) {
-    return;
-  }
-
-  if (typeDefNode->nodeClass != UA_NODECLASS_OBJECTTYPE) {
-    UA_Server_deleteNodeCopy(server, (void **) &typeDefNode);
-    return;
-  }
-  
-  // Create the object root as specified by the user
-  UA_NodeId objectRoot;
-  retval |= UA_Server_addObjectNode(server, UA_NODEID_NUMERIC(parentId.namespaceIndex, 0), objectCopy->browseName,
-                                    objectCopy->displayName, objectCopy->description, objectCopy->userWriteMask,
-                                    objectCopy->writeMask, parentId, referenceTypeId, typeDefinition, &objectRoot);
-  if (retval)
-    return;
-  
-  if (callback != UA_NULL)
-    callback(objectRoot, typeDefinition.nodeId, handle);
-  
-  // (1) If this node is a subtype of any other node, create its things first
-  UA_Server_addInstanceOf_inheritParentAttributes(server, subtypeRefs, componentRefs, objectRoot, callback, 
-                                                  (UA_ObjectTypeNode *) typeDefNode, instantiatedTypes, handle);
-  
-  // (2) For each object or variable referenced with hasComponent or hasProperty, create a new node of that
-  //     type for this objectRoot
-  UA_Server_addInstanceOf_instatiateChildNode(server, subtypeRefs, componentRefs, typedefRefs,
-                                              objectRoot, callback, (UA_ObjectTypeNode *) typeDefNode, 
-                                              UA_TRUE, instantiatedTypes, handle);
-  
-  return;
-}
-  
-void UA_Server_addInstanceOf_instatiateChildNode(UA_Server *server, arrayOfNodeIds *subtypeRefs, arrayOfNodeIds *componentRefs,
-                                                 arrayOfNodeIds *typedefRefs, UA_NodeId objectRoot, UA_InstantiationCallback callback,
-                                                 void *typeDefNode, UA_Boolean instantiateObjects, arrayOfNodeIds *instantiatedTypes,
-                                                 void *handle) {
-  UA_Boolean refTypeValid;
-  UA_NodeClass refClass;
-  UA_Node      *nodeClone;
-  UA_ExpandedNodeId *objectRootExpanded = UA_ExpandedNodeId_new();
-  UA_VariableNode *newVarNode;
-  UA_VariableTypeNode *varTypeNode;
-  UA_ReferenceNode ref;
-  UA_NodeId_copy(&objectRoot, &objectRootExpanded->nodeId );
-  UA_AddNodesResult adres;
-  for(int i=0; i< ((UA_ObjectTypeNode *) typeDefNode)->referencesSize; i++) {
-    ref = ((UA_ObjectTypeNode *) typeDefNode)->references[i];
-    if (ref.isInverse)
-      continue;
-    arrayOfNodeIds_idInArray((*componentRefs), ref.referenceTypeId, refTypeValid, UA_FALSE);
-    if (!refTypeValid) 
-      continue;
-    
-    // What type of node is this?
-    UA_Server_getNodeAttribute_nodeClass(server, ref.targetId.nodeId, &refClass);
-    switch (refClass) {
-      case UA_NODECLASS_VARIABLE: // Just clone the variable node with a new nodeId
-        UA_Server_getNodeCopy(server, ref.targetId.nodeId, (void **) &nodeClone);
-        if (nodeClone == UA_NULL)
-          break;
-        UA_NodeId_init(&nodeClone->nodeId);
-        nodeClone->nodeId.namespaceIndex = objectRoot.namespaceIndex;
-        if (nodeClone != UA_NULL) {
-          adres = UA_Server_addNode(server, nodeClone,  *objectRootExpanded, ref.referenceTypeId);
-          if (callback != UA_NULL)
-            callback(adres.addedNodeId, ref.targetId.nodeId, handle);
-        }
-        break;
-      case UA_NODECLASS_VARIABLETYPE: // Convert from a value protoype to a value, then add it
-        UA_Server_getNodeCopy(server, ref.targetId.nodeId, (void **) &varTypeNode);
-        newVarNode = UA_VariableNode_new();
-        newVarNode->nodeId.namespaceIndex = objectRoot.namespaceIndex;
-        UA_QualifiedName_copy(&varTypeNode->browseName, &varTypeNode->browseName);
-        UA_LocalizedText_copy(&varTypeNode->displayName, &varTypeNode->displayName);
-        UA_LocalizedText_copy(&varTypeNode->description, &varTypeNode->description);
-        newVarNode->writeMask = varTypeNode->writeMask;
-        newVarNode->userWriteMask = varTypeNode->userWriteMask;
-        newVarNode->valueRank = varTypeNode->valueRank;
-        
-        newVarNode->valueSource = varTypeNode->valueSource;
-        if (varTypeNode->valueSource == UA_VALUESOURCE_DATASOURCE)
-          newVarNode->value.dataSource = varTypeNode->value.dataSource;
-        else
-          UA_Variant_copy(&varTypeNode->value.variant, &newVarNode->value.variant);
-        
-        adres = UA_Server_addNode(server, (UA_Node *) newVarNode, *objectRootExpanded, ref.referenceTypeId);
-        if (callback != UA_NULL)
-          callback(adres.addedNodeId, ref.targetId.nodeId, handle);
-        UA_Server_deleteNodeCopy(server, (void **) &newVarNode);
-        UA_Server_deleteNodeCopy(server, (void **) &varTypeNode);
-        break;
-      case UA_NODECLASS_OBJECT: // An object may have it's own inheritance or child nodes
-        if (!instantiateObjects)
-          break;
-        
-        UA_Server_getNodeCopy(server, ref.targetId.nodeId, (void **) &nodeClone);
-        if (nodeClone == UA_NULL)
-          break; // switch
-          
-          // Retrieve this nodes type definition
-          UA_ExpandedNodeId_init(objectRootExpanded); // Slight misuse of an unsused ExpandedNodeId to encode the typeDefinition
-          int tidx;
-          for(tidx=0; tidx<nodeClone->referencesSize; tidx++) {
-            arrayOfNodeIds_idInArray((*typedefRefs), nodeClone->references[tidx].referenceTypeId, refTypeValid, UA_FALSE);
-            if (refTypeValid) 
-              break;
-          } // End iterate over nodeClone refs
-          
-          if (!refTypeValid) // This may be plain wrong, but since got this far...
-            objectRootExpanded->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE);
-          else
-            UA_ExpandedNodeId_copy(&nodeClone->references[tidx].targetId, objectRootExpanded);
-          
-          int lastArrayDepth = instantiatedTypes->size;
-          arrayOfNodeIds_addNodeId((*instantiatedTypes), ((UA_ObjectTypeNode *) typeDefNode)->nodeId);
-          
-          UA_Server_addInstanceOf_instatiateChildObject(server, subtypeRefs, componentRefs, typedefRefs, nodeClone,
-                                                        objectRoot, *objectRootExpanded, ref.referenceTypeId,
-                                                        callback, UA_TRUE, instantiatedTypes, handle);
-          instantiatedTypes->size = lastArrayDepth;
-          instantiatedTypes->ids = (UA_NodeId *) realloc(instantiatedTypes->ids, lastArrayDepth);
-          
-          UA_Server_deleteNodeCopy(server, (void **) &nodeClone);
-          UA_ExpandedNodeId_deleteMembers(objectRootExpanded); // since we only borrowed this, reset it
-          UA_NodeId_copy(&objectRoot, &objectRootExpanded->nodeId );
-          break;
-      case UA_NODECLASS_METHOD: // Link this method (don't clone the node)
-        UA_Server_addMonodirectionalReference(server, objectRoot, ref.targetId, ref.referenceTypeId, UA_TRUE);
-        break;
-      default:
-        break;
-    }
-  }
-  
-  if (objectRootExpanded != UA_NULL)
-    UA_ExpandedNodeId_delete(objectRootExpanded);
-  return;
-}
-
-UA_StatusCode UA_Server_appendInstanceOfSupertype(UA_Server *server, UA_NodeId nodeId, UA_NodeId appendToNodeId, 
-                                                  arrayOfNodeIds *subtypeRefs, arrayOfNodeIds *componentRefs, 
-                                                  UA_InstantiationCallback callback, arrayOfNodeIds *instantiatedTypes, 
-                                                  void *handle)  
-{
-  UA_StatusCode retval = UA_STATUSCODE_GOOD;
-
-  UA_Node *typeDefNode = UA_NULL;
-  UA_Server_getNodeCopy(server, nodeId, (void *) &typeDefNode);
-  if (typeDefNode == UA_NULL) {
-  return UA_STATUSCODE_BADTYPEDEFINITIONINVALID;
-  }
-
-  if (typeDefNode->nodeClass != UA_NODECLASS_OBJECTTYPE) {
-    UA_Server_deleteNodeCopy(server, (void **) &typeDefNode);
-    return UA_STATUSCODE_BADTYPEDEFINITIONINVALID;
-  }
-
-  UA_ExpandedNodeId *objectRootExpanded = UA_ExpandedNodeId_new();
-  UA_NodeId_copy(&appendToNodeId, &objectRootExpanded->nodeId );
-  // (1) If this node is a subtype of any other node, create its things first
-  UA_Server_addInstanceOf_inheritParentAttributes(server, subtypeRefs, componentRefs, appendToNodeId, callback, 
-                                                  (UA_ObjectTypeNode *) typeDefNode, instantiatedTypes, handle);
-
-  UA_Server_addInstanceOf_instatiateChildNode(server, subtypeRefs, componentRefs, UA_NULL, 
-                                              appendToNodeId, callback, (UA_ObjectTypeNode *) typeDefNode, 
-                                              UA_FALSE, instantiatedTypes, handle);
-  if (objectRootExpanded != UA_NULL)
-    UA_ExpandedNodeId_delete(objectRootExpanded);
-  return retval;
-}
-
-UA_StatusCode UA_Server_addInstanceOf(UA_Server *server, UA_NodeId nodeId, const UA_QualifiedName browseName,
-                        UA_LocalizedText displayName, UA_LocalizedText description, const UA_NodeId parentNodeId, 
-                        const UA_NodeId referenceTypeId, UA_UInt32 userWriteMask, UA_UInt32 writeMask, 
-                        const UA_ExpandedNodeId typeDefinition, UA_InstantiationCallback callback, void *handle, 
-                        UA_NodeId *createdNodeId) 
-{
-  UA_StatusCode retval = UA_STATUSCODE_GOOD;
-  
-  UA_Node *typeDefNode = UA_NULL;
-  UA_Server_getNodeCopy(server, typeDefinition.nodeId, (void *) &typeDefNode);
-  
-  if (typeDefNode == UA_NULL) {
-    return UA_STATUSCODE_BADTYPEDEFINITIONINVALID;
-  }
-
-  if (typeDefNode->nodeClass != UA_NODECLASS_OBJECTTYPE) {
-    UA_Server_deleteNodeCopy(server, (void **) &typeDefNode);
-    return UA_STATUSCODE_BADTYPEDEFINITIONINVALID;
-  }
-  
-  // Create the object root as specified by the user
-  UA_NodeId objectRoot;
-  retval |= UA_Server_addObjectNode(server, nodeId, browseName, displayName, description, userWriteMask, writeMask,
-                          parentNodeId, referenceTypeId,
-                          typeDefinition, &objectRoot
-  );
-  if (retval)
-    return retval;
-  
-  // These refs will be examined later. 
-  // FIXME: Create these arrays dynamically to include any subtypes as well
-  arrayOfNodeIds subtypeRefs = (arrayOfNodeIds) {
-    .size  = 1,
-    .ids   = (UA_NodeId[]) { UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE)}
-  };
-  arrayOfNodeIds componentRefs = (arrayOfNodeIds) {
-    .size = 2,
-    .ids  = (UA_NodeId[]) { UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY)}
-  };
-  arrayOfNodeIds typedefRefs = (arrayOfNodeIds) {
-    .size = 1,
-    .ids  = (UA_NodeId[]) { UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION)}
-  };
-  
-  UA_ExpandedNodeId *objectRootExpanded = UA_ExpandedNodeId_new();
-  UA_NodeId_copy(&objectRoot, &objectRootExpanded->nodeId );
-  
-  arrayOfNodeIds instantiatedTypes = (arrayOfNodeIds ) {.size=0, .ids=NULL};
-  arrayOfNodeIds_addNodeId(instantiatedTypes, typeDefNode->nodeId);
-  
-  // (1) If this node is a subtype of any other node, create its things first
-  UA_Server_addInstanceOf_inheritParentAttributes(server, &subtypeRefs, &componentRefs, objectRoot, callback, 
-                                                  (UA_ObjectTypeNode *) typeDefNode, &instantiatedTypes, handle);
-  
-  // (2) For each object or variable referenced with hasComponent or hasProperty, create a new node of that
-  //     type for this objectRoot
-  UA_Server_addInstanceOf_instatiateChildNode(server, &subtypeRefs, &componentRefs, &typedefRefs,
-                                              objectRoot, callback, (UA_ObjectTypeNode *) typeDefNode, 
-                                              UA_TRUE, &instantiatedTypes, handle);
-  arrayOfNodeIds_deleteMembers(instantiatedTypes);
-  
-  UA_ExpandedNodeId_delete(objectRootExpanded);
-  UA_Server_deleteNodeCopy(server, (void **) &typeDefNode);
-  return retval;
-}
-

+ 2 - 2
src/server/ua_services.h

@@ -215,8 +215,8 @@ void Service_Read_single(UA_Server *server, UA_Session *session, UA_TimestampsTo
 void Service_Write(UA_Server *server, UA_Session *session, const UA_WriteRequest *request,
                    UA_WriteResponse *response);
 
-/** Single attribute writes are exposed to the userspace */
-UA_StatusCode Service_Write_single(UA_Server *server, UA_Session *session, const UA_WriteValue *wvalue);
+/** Single attribute writes are exposed to the userspace. The wvalue may be destroyed (deleteMembers) */
+UA_StatusCode Service_Write_single(UA_Server *server, UA_Session *session, UA_WriteValue *wvalue);
 
 // Service_HistoryUpdate
 /** @} */

+ 136 - 114
src/server/ua_services_attribute.c

@@ -428,31 +428,104 @@ void Service_Read(UA_Server *server, UA_Session *session, const UA_ReadRequest *
 		}
 
 #define CHECK_NODECLASS_WRITE(CLASS)                                    \
-		if((anyTypeNode.node->nodeClass & (CLASS)) == 0) {				\
-			retval |= UA_STATUSCODE_BADNODECLASSINVALID;				\
-			break;														\
-		}
+    if((editable->nodeClass & (CLASS)) == 0) {                          \
+        retval |= UA_STATUSCODE_BADNODECLASSINVALID;                    \
+        break;                                                          \
+    }
+
+/* clone the node and replace the value. orig can also be a variabletypenode. */
+static UA_StatusCode copyValueIntoNode(UA_VariableNode *node, UA_WriteValue *wvalue) {
+    UA_StatusCode retval = UA_STATUSCODE_GOOD;
+
+    /* parse the range */
+    UA_Boolean hasRange = UA_FALSE;
+    UA_NumericRange range;
+    if(wvalue->indexRange.length > 0) {
+        retval = parse_numericrange(wvalue->indexRange, &range);
+        if(retval != UA_STATUSCODE_GOOD)
+            goto clean_up;
+        hasRange = UA_TRUE;
+    }
+
+    /* the relevant members are similar for variables and variabletypes */
+    UA_Variant *oldV = &node->value.variant;
+    UA_Variant *newV = &wvalue->value.value;
+    if(node->valueSource == UA_VALUESOURCE_VARIANT) {
+
+        /* the nodeid on the wire may be != the nodeid in the node: opaque types, enums and bytestrings */
+        UA_WriteValue cast_wv;
+        if(!UA_NodeId_equal(&oldV->type->typeId, &newV->type->typeId)) {
+            cast_wv = *wvalue;
+            wvalue = &cast_wv;
+            newV = &cast_wv.value.value;
+            if(oldV->type->namespaceZero && newV->type->namespaceZero &&
+               oldV->type->typeIndex == newV->type->typeIndex) {
+                /* An enum was sent as an int32, or an opaque type as a bytestring. This is
+                   detected with the typeIndex indicated the "true" datatype. */
+                cast_wv.value.value.type = oldV->type;
+            } else if(oldV->type == &UA_TYPES[UA_TYPES_BYTE] && !UA_Variant_isScalar(oldV) &&
+                      newV->type == &UA_TYPES[UA_TYPES_BYTESTRING] && UA_Variant_isScalar(newV)) {
+                /* a string is written to a byte array */
+                UA_ByteString *str = (UA_ByteString*) newV->data;
+                newV->arrayLength = str->length;
+                newV->data = str->data;
+                newV->type = &UA_TYPES[UA_TYPES_BYTE];
+            } else {
+                retval = UA_STATUSCODE_BADTYPEMISMATCH;
+                goto clean_up;
+            }
+        }
+        
+        /* insert the value */
+        if(!hasRange) {
+            /* don't copy, take the pointers and invalidate the write_value */
+            UA_Variant_deleteMembers(oldV);
+            *oldV = *newV;
+            UA_Variant_init(newV);
+        } else {
+            retval = UA_Variant_setRange(oldV, newV->data, newV->arrayLength, range);
+        }
+
+    } else /* node->valueSource == UA_VALUESOURCE_DATASOURCE */ {
+        if(!node->value.dataSource.write) {
+            retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
+            goto clean_up;
+        }
+        if(!hasRange)
+            retval = node->value.dataSource.write(node->value.dataSource.handle, node->nodeId, newV, UA_NULL);
+        else
+            retval = node->value.dataSource.write(node->value.dataSource.handle, node->nodeId, newV, &range);
+    }
 
-UA_StatusCode Service_Write_single(UA_Server *server, UA_Session *session, const UA_WriteValue *wvalue) {
+ clean_up:
+    if(hasRange)
+        UA_free(range.dimensions);
+    return retval;
+}
+
+UA_StatusCode Service_Write_single(UA_Server *server, UA_Session *session, UA_WriteValue *wvalue) {
 	UA_assert(server != UA_NULL && session != UA_NULL && wvalue != UA_NULL);
+    if(!wvalue->value.hasValue)
+        return UA_STATUSCODE_BADNODATA; // TODO: is this the right return code?
+    const UA_Node *orig;
+ retryWriteSingle:
+    orig = UA_NodeStore_get(server->nodestore, &wvalue->nodeId);
+    if(!orig)
+        return UA_STATUSCODE_BADNODEIDUNKNOWN;
+
+#ifndef UA_MULTITHREADING
+    /* We cheat if multithreading is not enabled and treat the node as mutable. */
+    UA_Node *editable = (UA_Node*)(uintptr_t)orig;
+#else
+    UA_Node *editable = UA_Node_copyAnyNodeClass(orig);
+    if(!new) {
+        UA_NodeStore_release(orig);
+        return UA_STATUSCODE_BADOUTOFMEMORY;
+    }
+#endif
 
-	union {
-		UA_Node *node;
-		UA_ObjectNode *oObj;
-		UA_ObjectTypeNode *otObj;
-		UA_VariableNode *vObj;
-		UA_VariableTypeNode *vtObj;
-		UA_ReferenceTypeNode *rtObj;
-		UA_MethodNode *mObj;
-		UA_DataTypeNode *dtObj;
-		UA_ViewNode *vwObj;
-	} anyTypeNode;
-
-	UA_StatusCode retval = UA_Server_getNodeCopy(server, wvalue->nodeId, (void **) &anyTypeNode.node);
-	if (retval)
-		return retval;
-  
-	void *value = wvalue->value.value.data;
+    void *value = wvalue->value.value.data;
+    UA_StatusCode retval = UA_STATUSCODE_GOOD;
 	switch(wvalue->attributeId) {
     case UA_ATTRIBUTEID_NODEID:
     case UA_ATTRIBUTEID_NODECLASS:
@@ -461,162 +534,111 @@ UA_StatusCode Service_Write_single(UA_Server *server, UA_Session *session, const
 		break;
 	case UA_ATTRIBUTEID_BROWSENAME:
 		CHECK_DATATYPE(QUALIFIEDNAME);
-		UA_QualifiedName_deleteMembers(&anyTypeNode.node->browseName);
-		UA_QualifiedName_copy((const UA_QualifiedName *) value, &anyTypeNode.node->browseName);
+		UA_QualifiedName_deleteMembers(&editable->browseName);
+		UA_QualifiedName_copy((UA_QualifiedName*)value, &editable->browseName);
 		break;
 	case UA_ATTRIBUTEID_DISPLAYNAME:
 		CHECK_DATATYPE(LOCALIZEDTEXT);
-		UA_LocalizedText_deleteMembers(&anyTypeNode.node->displayName);
-		UA_LocalizedText_copy((const UA_LocalizedText *) value, &anyTypeNode.node->displayName);
+		UA_LocalizedText_deleteMembers(&editable->displayName);
+		UA_LocalizedText_copy((UA_LocalizedText*)value, &editable->displayName);
 		break;
 	case UA_ATTRIBUTEID_DESCRIPTION:
 		CHECK_DATATYPE(LOCALIZEDTEXT);
-		UA_LocalizedText_deleteMembers(&anyTypeNode.node->description);
-		UA_LocalizedText_copy((const UA_LocalizedText *) value, &anyTypeNode.node->description);
+		UA_LocalizedText_deleteMembers(&editable->description);
+		UA_LocalizedText_copy((UA_LocalizedText*)value, &editable->description);
 		break;
 	case UA_ATTRIBUTEID_WRITEMASK:
 		CHECK_DATATYPE(UINT32);
-		anyTypeNode.node->writeMask = *(const UA_UInt32*) value;
+		editable->writeMask = *(UA_UInt32*)value;
 		break;
 	case UA_ATTRIBUTEID_USERWRITEMASK:
 		CHECK_DATATYPE(UINT32);
-		anyTypeNode.node->userWriteMask = *(const UA_UInt32*) value;
+		editable->userWriteMask = *(UA_UInt32*)value;
 		break;    
 	case UA_ATTRIBUTEID_ISABSTRACT:
-		CHECK_NODECLASS_WRITE(UA_NODECLASS_OBJECTTYPE | UA_NODECLASS_REFERENCETYPE | UA_NODECLASS_VARIABLETYPE | UA_NODECLASS_DATATYPE);
+		CHECK_NODECLASS_WRITE(UA_NODECLASS_OBJECTTYPE | UA_NODECLASS_REFERENCETYPE |
+                              UA_NODECLASS_VARIABLETYPE | UA_NODECLASS_DATATYPE);
 		CHECK_DATATYPE(BOOLEAN);
-		anyTypeNode.otObj->isAbstract = *(const UA_Boolean *) value;
+		((UA_ObjectTypeNode*)editable)->isAbstract = *(UA_Boolean*)value;
 		break;
 	case UA_ATTRIBUTEID_SYMMETRIC:
 		CHECK_NODECLASS_WRITE(UA_NODECLASS_REFERENCETYPE);
 		CHECK_DATATYPE(BOOLEAN);
-		anyTypeNode.rtObj->symmetric = *(const UA_Boolean *) value;
+		((UA_ReferenceTypeNode*)editable)->symmetric = *(UA_Boolean*)value;
 		break;
 	case UA_ATTRIBUTEID_INVERSENAME:
 		CHECK_NODECLASS_WRITE(UA_NODECLASS_REFERENCETYPE);
 		CHECK_DATATYPE(LOCALIZEDTEXT);
-		UA_LocalizedText_deleteMembers(&anyTypeNode.rtObj->inverseName);
-		UA_LocalizedText_copy((const UA_LocalizedText *) value, &anyTypeNode.rtObj->inverseName);
+        UA_ReferenceTypeNode *n = (UA_ReferenceTypeNode*)editable;
+		UA_LocalizedText_deleteMembers(&n->inverseName);
+		UA_LocalizedText_copy((UA_LocalizedText*)value, &n->inverseName);
 		break;
 	case UA_ATTRIBUTEID_CONTAINSNOLOOPS:
 		CHECK_NODECLASS_WRITE(UA_NODECLASS_VIEW);
 		CHECK_DATATYPE(BOOLEAN);
-		anyTypeNode.vwObj->containsNoLoops = *(const UA_Boolean *) value;
+        ((UA_ViewNode*)editable)->containsNoLoops = *(UA_Boolean*)value;
 		break;
 	case UA_ATTRIBUTEID_EVENTNOTIFIER:
 		CHECK_NODECLASS_WRITE(UA_NODECLASS_VIEW | UA_NODECLASS_OBJECT);
 		CHECK_DATATYPE(BYTE);
-		anyTypeNode.vwObj->eventNotifier = *(const UA_Byte *) value;
+        ((UA_ViewNode*)editable)->eventNotifier = *(UA_Byte*)value;
 		break;
 	case UA_ATTRIBUTEID_VALUE:
 		CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
-
-		/* parse the range */
-		UA_Boolean hasRange = UA_FALSE;
-		UA_NumericRange range;
-		if(wvalue->indexRange.length > 0) {
-			retval = parse_numericrange(wvalue->indexRange, &range);
-			if(retval != UA_STATUSCODE_GOOD)
-				break;
-			hasRange = UA_TRUE;
-		}
-
-		/* the relevant members are similar for variables and variabletypes */
-		UA_VariableNode *vn = anyTypeNode.vObj;
-		if(vn->valueSource == UA_VALUESOURCE_DATASOURCE) {
-			if(!vn->value.dataSource.write) {
-				retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
-				goto clean_up_range;
-			}
-			if(hasRange)
-				retval = vn->value.dataSource.write(vn->value.dataSource.handle, vn->nodeId, &wvalue->value.value, &range);
-			else
-				retval = vn->value.dataSource.write(vn->value.dataSource.handle, vn->nodeId, &wvalue->value.value, UA_NULL);
-			goto clean_up_range;
-		}
-
-		/* the nodeid on the wire may be != the nodeid in the node: opaque types, enums and bytestrings */
-		// TODO: fix the dirty hack of writing into const
-		UA_Variant *oldV = &vn->value.variant;
-		UA_WriteValue *wv = (UA_WriteValue*)(uintptr_t)wvalue;
-		if(!UA_NodeId_equal(&oldV->type->typeId, &wvalue->value.value.type->typeId)) {
-			if(oldV->type->namespaceZero && wvalue->value.value.type->namespaceZero &&
-			   oldV->type->typeIndex == wvalue->value.value.type->typeIndex)
-				/* An enum was sent as an int32, or an opaque type as a bytestring. This is
-				   detected with the typeIndex indicated the "true" datatype. */
-				wv->value.value.type = oldV->type;
-			else if(oldV->type == &UA_TYPES[UA_TYPES_BYTE] && !UA_Variant_isScalar(oldV) &&
-					wvalue->value.value.type == &UA_TYPES[UA_TYPES_BYTESTRING] &&
-					UA_Variant_isScalar(&wvalue->value.value)) {
-				/* a string is written to a byte array */
-				UA_ByteString *str = (UA_ByteString*) wvalue->value.value.data;
-				wv->value.value.arrayLength = str->length;
-				wv->value.value.data = str->data;
-				wv->value.value.type = &UA_TYPES[UA_TYPES_BYTE];
-				UA_free(str);
-			} else {
-				retval = UA_STATUSCODE_BADTYPEMISMATCH;
-				goto clean_up_range;
-			}
-		}
-
-		/* insert the new value */
-		if(hasRange)
-			retval = UA_Variant_setRangeCopy(&vn->value.variant, wvalue->value.value.data, wvalue->value.value.arrayLength, range);
-		else {
-			UA_Variant_deleteMembers(&vn->value.variant);
-			retval = UA_Variant_copy(&wvalue->value.value, &vn->value.variant);
-		}
-
-	clean_up_range:
-		if(hasRange)
-			UA_free(range.dimensions);
-
+        retval = copyValueIntoNode((UA_VariableNode*)editable, wvalue);
 		break;
 	case UA_ATTRIBUTEID_ACCESSLEVEL:
 		CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE);
 		CHECK_DATATYPE(BYTE);
-		anyTypeNode.vObj->accessLevel = *(const UA_Byte*) value;
+		((UA_VariableNode*)editable)->accessLevel = *(UA_Byte*)value;
 		break;
 	case UA_ATTRIBUTEID_USERACCESSLEVEL:
 		CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE);
 		CHECK_DATATYPE(BYTE);
-		anyTypeNode.vObj->userAccessLevel = *(const UA_Byte*) value;
+		((UA_VariableNode*)editable)->userAccessLevel = *(UA_Byte*)value;
 		break;
 	case UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL:
 		CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE);
 		CHECK_DATATYPE(DOUBLE);
-		anyTypeNode.vObj->minimumSamplingInterval = *(const UA_Double *) value;
+		((UA_VariableNode*)editable)->minimumSamplingInterval = *(UA_Double*)value;
 		break;
 	case UA_ATTRIBUTEID_HISTORIZING:
 		CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE);
 		CHECK_DATATYPE(BOOLEAN);
-		anyTypeNode.vObj->historizing = *(const UA_Boolean *) value;
+		((UA_VariableNode*)editable)->historizing = *(UA_Boolean*)value;
 		break;
 	case UA_ATTRIBUTEID_EXECUTABLE:
 		CHECK_NODECLASS_WRITE(UA_NODECLASS_METHOD);
 		CHECK_DATATYPE(BOOLEAN);
-		anyTypeNode.mObj->executable = *(const UA_Boolean *) value;
+		((UA_MethodNode*)editable)->executable = *(UA_Boolean*)value;
 		break;
 	case UA_ATTRIBUTEID_USEREXECUTABLE:
 		CHECK_NODECLASS_WRITE(UA_NODECLASS_METHOD);
 		CHECK_DATATYPE(BOOLEAN);
-		anyTypeNode.mObj->userExecutable = *(const UA_Boolean *) value;
+		((UA_MethodNode*)editable)->userExecutable = *(UA_Boolean*)value;
 		break;
 	default:
 		retval = UA_STATUSCODE_BADATTRIBUTEIDINVALID;
 		break;
 	}
 
-	if(retval != UA_STATUSCODE_GOOD) {
-		UA_Server_deleteNodeCopy(server, (void **) &anyTypeNode.node);
-		return retval;
-	}
-  
-	const UA_Node *oldNode = UA_NodeStore_get(server->nodestore, &wvalue->nodeId);
-	// Node copy is now in the nodestore. Do not delete here!
-	retval = UA_NodeStore_replace(server->nodestore, oldNode, anyTypeNode.node, UA_NULL);
-	UA_NodeStore_release(oldNode);
+#ifndef UA_MULTITHREADING
+    UA_NodeStore_release(orig);
+#else
+    if(retval != UA_STATUSCODE_GOOD) {
+        UA_NodeStore_release(orig);
+        UA_Node_deleteAnyNodeClass(editable);
+        return retval;
+    }
+       
+    retval = UA_NodeStore_replace(server->nodestore, orig, editable, UA_NULL);
+	UA_NodeStore_release(orig);
+    if(retval != UA_STATUSCODE_GOOD) {
+        /* The node was replaced under our feet. Retry. */
+        UA_Node_deleteAnyNodeClass(editable);
+        goto retryWriteSingle;
+    }
+#endif
 	return retval;
 }
 

+ 19 - 24
tests/check_services_attributes.c

@@ -37,13 +37,15 @@ static UA_Server* makeTestSequence(void) {
     const UA_NodeId myIntegerNodeId = UA_NODEID_STRING(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, myIntegerName, UA_LOCALIZEDTEXT("en_US","the answer"), UA_LOCALIZEDTEXT("en_US","the answer"), 0, 0,
-                              parentNodeId, parentReferenceNodeId, myIntegerVariant, NULL);
-
+    UA_Server_addVariableNode(server, myIntegerNodeId, myIntegerName, UA_LOCALIZEDTEXT("en_US","the answer"),
+                              UA_LOCALIZEDTEXT("en_US","the answer"), 0, 0, parentNodeId,
+                              parentReferenceNodeId, myIntegerVariant, NULL);
     /* ObjectNode */
-    UA_Server_addObjectNode(server, UA_NODEID_NUMERIC(1, 50), UA_QUALIFIEDNAME(1, "Demo"), UA_LOCALIZEDTEXT("en_US","Demo"), 
-                            UA_LOCALIZEDTEXT("en_US","Demo"), 0, 0, UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
-                            UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), NULL);
+    UA_Server_addObjectNode(server, UA_NODEID_NUMERIC(1, 50), UA_QUALIFIEDNAME(1, "Demo"),
+                            UA_LOCALIZEDTEXT("en_US","Demo"), UA_LOCALIZEDTEXT("en_US","Demo"), 0, 0,
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
+                            UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), NULL);
 
     /* ReferenceTypeNode */
     UA_ReferenceTypeNode *organizes = UA_ReferenceTypeNode_new();
@@ -52,26 +54,24 @@ static UA_Server* makeTestSequence(void) {
     organizes->nodeId.identifier.numeric = UA_NS0ID_ORGANIZES;
     organizes->isAbstract = UA_FALSE;
     organizes->symmetric  = UA_FALSE;
-    UA_Server_addNode(server, (UA_Node*)organizes, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_HIERARCHICALREFERENCES), UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE));
+    UA_Server_addNode(server, (UA_Node*)organizes,
+                          UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_HIERARCHICALREFERENCES),
+                          UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE));
 
     /* ViewNode */
     UA_ViewNode *viewtest = UA_ViewNode_new();
     copyNames((UA_Node*)viewtest, "Viewtest");
     viewtest->nodeId.identifier.numeric = UA_NS0ID_VIEWNODE;
-
-    UA_Server_addNode(server, (UA_Node*)viewtest,
-                      UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_VIEWSFOLDER),
+    UA_Server_addNode(server, (UA_Node*)viewtest, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_VIEWSFOLDER),
                       UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE));
 
 	/* MethodNode */
     UA_MethodNode *methodtest = UA_MethodNode_new();
     copyNames((UA_Node*)methodtest, "Methodtest");
     methodtest->nodeId.identifier.numeric = UA_NS0ID_METHODNODE;
-
     UA_Server_addNode(server, (UA_Node*)methodtest,
                       UA_EXPANDEDNODEID_NUMERIC(0, 3),
                       UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE));
-
 	return server;
 }
 
@@ -79,19 +79,18 @@ static UA_VariableNode* makeCompareSequence(void) {
 	UA_VariableNode *node = UA_VariableNode_new();
 	UA_Variant *myIntegerVariant = UA_Variant_new();
 	UA_Int32 myInteger = 42;
-	UA_Variant_setScalarCopy(myIntegerVariant, &myInteger,
-			&UA_TYPES[UA_TYPES_INT32]);
+	UA_Variant_setScalarCopy(myIntegerVariant, &myInteger, &UA_TYPES[UA_TYPES_INT32]);
 	const UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME(1, "the answer");
 	const UA_LocalizedText myIntegerDisplName = UA_LOCALIZEDTEXT("en_US", "the answer");
-        const UA_NodeId myIntegerNodeId = UA_NODEID_STRING(1, "the.answer");
+    const UA_NodeId myIntegerNodeId = UA_NODEID_STRING(1, "the.answer");
 	UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
 	//UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
 	node->value.variant=*myIntegerVariant;
 	UA_NodeId_copy(&myIntegerNodeId,&node->nodeId);
 	UA_QualifiedName_copy(&myIntegerName,&node->browseName);
-        UA_LocalizedText_copy(&myIntegerDisplName, &node->displayName);
-        UA_LocalizedText_copy(&myIntegerDisplName, &node->description);
-        UA_ExpandedNodeId parentId;
+    UA_LocalizedText_copy(&myIntegerDisplName, &node->displayName);
+    UA_LocalizedText_copy(&myIntegerDisplName, &node->description);
+    UA_ExpandedNodeId parentId;
 	UA_ExpandedNodeId_init(&parentId);
 	UA_NodeId_copy(&parentNodeId,&parentId.nodeId);
 	return node;
@@ -736,12 +735,8 @@ START_TEST(WriteSingleAttributeValue) {
 		Service_Read_single(server, &adminSession, UA_TIMESTAMPSTORETURN_NEITHER, &rReq.nodesToRead[0], &resp);
 		ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
 		ck_assert(wValue.value.hasValue);
-		const UA_Node *node = UA_NodeStore_get(server->nodestore, &wValue.nodeId);
-		ck_assert_int_eq(node->nodeClass, UA_NODECLASS_VARIABLE);
-		const UA_VariableNode *vn = (const UA_VariableNode*)node;
-		const UA_Variant *oldV = &vn->value.variant;
-		ck_assert_ptr_eq(&oldV->type->typeId, &wValue.value.value.type->typeId);
-		ck_assert_int_eq(20, *(UA_Int32* )resp.value.data);
+		ck_assert_int_eq(20, *(UA_Int32*)resp.value.data);
+        UA_DataValue_deleteMembers(&resp);
         UA_Server_delete(server);
 } END_TEST