Przeglądaj źródła

HPCC-19216 Dynamically add <Properties> section from buildset

Add support to dynamically add <Properties> sections to
ESPService and ESPBinding sections in the environment

Signed-off-by: Ken Rowland <kenneth.rowland@lexisnexisrisk.com>
Ken Rowland 7 lat temu
rodzic
commit
67b5147733
44 zmienionych plików z 1649 dodań i 319 usunięć
  1. 4 0
      configuration/config2/CMakeLists.txt
  2. 117 0
      configuration/config2/EnvironmentEventHandlers.cpp
  3. 97 0
      configuration/config2/EnvironmentEventHandlers.hpp
  4. 46 0
      configuration/config2/EnvironmentLoader.hpp
  5. 126 22
      configuration/config2/EnvironmentMgr.cpp
  6. 9 5
      configuration/config2/EnvironmentMgr.hpp
  7. 150 10
      configuration/config2/EnvironmentNode.cpp
  8. 11 4
      configuration/config2/EnvironmentNode.hpp
  9. 14 5
      configuration/config2/EnvironmentValue.cpp
  10. 39 0
      configuration/config2/InsertableItem.cpp
  11. 42 0
      configuration/config2/InsertableItem.hpp
  12. 89 24
      configuration/config2/SchemaItem.cpp
  13. 12 4
      configuration/config2/SchemaItem.hpp
  14. 0 15
      configuration/config2/SchemaParser.cpp
  15. 0 1
      configuration/config2/SchemaParser.hpp
  16. 1 1
      configuration/config2/SchemaTypeIntegerLimits.cpp
  17. 20 1
      configuration/config2/SchemaTypeLimits.cpp
  18. 14 0
      configuration/config2/SchemaTypeLimits.hpp
  19. 1 1
      configuration/config2/SchemaTypeStringLimits.cpp
  20. 1 1
      configuration/config2/SchemaTypeStringLimits.hpp
  21. 23 9
      configuration/config2/SchemaValue.cpp
  22. 9 1
      configuration/config2/SchemaValue.hpp
  23. 1 1
      configuration/config2/Status.cpp
  24. 4 3
      configuration/config2/Status.hpp
  25. 35 0
      configuration/config2/Utils.cpp
  26. 26 0
      configuration/config2/Utils.hpp
  27. 126 0
      configuration/config2/XMLEnvironmentLoader.cpp
  28. 44 0
      configuration/config2/XMLEnvironmentLoader.hpp
  29. 13 23
      configuration/config2/XMLEnvironmentMgr.cpp
  30. 2 2
      configuration/config2/XMLEnvironmentMgr.hpp
  31. 188 84
      configuration/config2/XSDSchemaParser.cpp
  32. 2 1
      configuration/config2/XSDSchemaParser.hpp
  33. 1 1
      configuration/config2/XSDValueSetParser.cpp
  34. 34 10
      configuration/config2/test.cpp
  35. 40 3
      esp/scm/ws_config2.ecm
  36. 1 0
      esp/services/ws_config2/ws_config2Error.hpp
  37. 68 8
      esp/services/ws_config2/ws_config2Service.cpp
  38. 1 0
      esp/services/ws_config2/ws_config2Service.hpp
  39. 1 1
      esp/services/ws_config2/ws_config2_plugin.cpp
  40. 10 10
      initfiles/componentfiles/config2xml/environment.xsd
  41. 63 62
      initfiles/componentfiles/config2xml/esp.xsd
  42. 94 3
      initfiles/componentfiles/config2xml/esp_service_smc.xsd
  43. 3 1
      initfiles/componentfiles/config2xml/esp_service_wsecl2.xsd
  44. 67 2
      initfiles/componentfiles/config2xml/types.xsd

+ 4 - 0
configuration/config2/CMakeLists.txt

@@ -20,6 +20,7 @@ project ( config2 )
 SET(  SRCS
       EnvironmentMgr.cpp
       XMLEnvironmentMgr.cpp
+      XMLEnvironmentLoader.cpp
       SchemaParser.cpp
       SchemaItem.cpp
       SchemaValue.cpp
@@ -32,6 +33,9 @@ SET(  SRCS
       XSDSchemaParser.cpp
       XSDValueSetParser.cpp
       Status.cpp
+      EnvironmentEventHandlers.cpp
+      Utils.cpp
+      InsertableItem.cpp
 )
 
 INCLUDE_DIRECTORIES(

+ 117 - 0
configuration/config2/EnvironmentEventHandlers.cpp

@@ -0,0 +1,117 @@
+/*##############################################################################
+
+HPCC SYSTEMS software Copyright (C) 2018 HPCC Systems®.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+############################################################################## */
+
+#include "EnvironmentEventHandlers.hpp"
+#include "EnvironmentNode.hpp"
+#include "EnvironmentValue.hpp"
+
+
+bool CreateEnvironmentEventHandler::handleEvent(const std::string &eventType, std::shared_ptr<EnvironmentNode> pEventNode)
+{
+    return pEventNode->getSchemaItem()->getItemType() == m_itemType;
+}
+
+
+void AttributeDependencyCreateEventHandler::addDependency(const std::string &attrName, const std::string &attrVal, const std::string &depAttr, const std::string &depVal)
+{
+    auto valSetMapIt = m_depAttrVals.find(attrName);
+    if (valSetMapIt == m_depAttrVals.end())
+    {
+        std::map<std::string, std::pair<std::string, std::string>> keyValMap;
+        m_depAttrVals[attrName] = keyValMap;
+    }
+
+    m_depAttrVals[attrName][attrVal] = std::pair<std::string, std::string>(depAttr, depVal);
+}
+
+
+bool AttributeDependencyCreateEventHandler::handleEvent(const std::string &eventType, std::shared_ptr<EnvironmentNode> pEventNode)
+{
+    bool rc = false;
+    if (CreateEnvironmentEventHandler::handleEvent(eventType, pEventNode))
+    {
+        for (auto attrIt = m_depAttrVals.begin(); attrIt != m_depAttrVals.end(); ++attrIt)
+        {
+            std::shared_ptr<EnvironmentValue> pAttr = pEventNode->getAttribute(attrIt->first);
+            if (pAttr && pAttr->getSchemaValue()->getType()->isEnumerated())
+            {
+                rc = true;   // we handled at least one
+                for (auto valueIt = attrIt->second.begin(); valueIt != attrIt->second.end(); ++valueIt)
+                {
+                    pAttr->getSchemaValue()->getType()->getLimits()->addDependentAttributeValue(valueIt->first, valueIt->second.first, valueIt->second.second);
+                }
+            }
+        }
+    }
+    return rc;
+}
+
+
+void InsertEnvironmentDataCreateEventHandler::setItemAttributeName(const std::string &name)
+{
+    m_itemAttribute = name;
+    if (m_matchAttribute.empty())
+    {
+        m_matchAttribute = name;
+    }
+}
+
+
+bool InsertEnvironmentDataCreateEventHandler::handleEvent(const std::string &eventType, std::shared_ptr<EnvironmentNode> pEventNode)
+{
+    bool rc = false;
+    if (CreateEnvironmentEventHandler::handleEvent(eventType, pEventNode))
+    {
+        if (!m_itemAttribute.empty())
+        {
+            std::vector<std::shared_ptr<EnvironmentNode>> matchNodes;
+
+            if (!m_matchPath.empty())
+            {
+                pEventNode->fetchNodes(m_matchPath, matchNodes);
+            }
+            else
+            {
+                matchNodes.push_back(pEventNode);
+            }
+
+            for (auto nodeIt = matchNodes.begin(); nodeIt != matchNodes.end(); ++nodeIt)
+            {
+                if (!m_itemAttribute.empty())
+                {
+                    std::shared_ptr<EnvironmentValue> pItemAttr = pEventNode->getAttribute(m_itemAttribute);
+                    if (pItemAttr)
+                    {
+                        std::shared_ptr<EnvironmentValue> pMatchAttr = (*nodeIt)->getAttribute(m_matchAttribute);
+                        if (pMatchAttr)
+                        {
+                            if (pMatchAttr->getValue() == pItemAttr->getValue())
+                            {
+                                pEventNode->addEnvironmentInsertData(m_envData);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        else
+        {
+            pEventNode->addEnvironmentInsertData(m_envData);
+        }
+    }
+    return rc;
+}

+ 97 - 0
configuration/config2/EnvironmentEventHandlers.hpp

@@ -0,0 +1,97 @@
+/*##############################################################################
+
+HPCC SYSTEMS software Copyright (C) 2018 HPCC Systems®.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+############################################################################## */
+
+
+#ifndef _CONFIG2_CONFIGEVENTS_HPP_
+#define _CONFIG2_CONFIGEVENTS_HPP_
+
+#include <string>
+#include <map>
+#include <memory>
+
+class EnvironmentNode;
+
+class EnvironmentEventHandler
+{
+    public:
+
+        EnvironmentEventHandler(const std::string &type) : m_eventType(type) {}
+        virtual ~EnvironmentEventHandler() {}
+        virtual bool handleEvent(const std::string &eventType, std::shared_ptr<EnvironmentNode> pEnvNode) { return false; }
+
+
+    protected:
+
+        std::string m_eventType;
+};
+
+
+class CreateEnvironmentEventHandler : public EnvironmentEventHandler
+{
+    public:
+
+        CreateEnvironmentEventHandler() : EnvironmentEventHandler("create") {}
+        virtual ~CreateEnvironmentEventHandler() {}
+        void setItemType(const std::string &type) { m_itemType = type; }
+
+        virtual bool handleEvent(const std::string &eventType, std::shared_ptr<EnvironmentNode> pEnvNode);
+
+
+    protected:
+
+        std::string m_itemType;
+};
+
+
+class AttributeDependencyCreateEventHandler : public CreateEnvironmentEventHandler
+{
+    public:
+
+        AttributeDependencyCreateEventHandler() {}
+        virtual ~AttributeDependencyCreateEventHandler() {}
+        void addDependency(const std::string &attrName, const std::string &attrValr, const std::string &depAttr, const std::string &depVal);
+        virtual bool handleEvent(const std::string &eventType, std::shared_ptr<EnvironmentNode> pEnvNode);
+
+
+    protected:
+
+        std::map<std::string, std::map<std::string, std::pair<std::string, std::string>>> m_depAttrVals;
+};
+
+
+class InsertEnvironmentDataCreateEventHandler : public CreateEnvironmentEventHandler
+{
+    public:
+
+        InsertEnvironmentDataCreateEventHandler() {}
+        virtual ~InsertEnvironmentDataCreateEventHandler() {}
+        void setEnvironmentInsertData(const std::string &envData) { m_envData = envData;  }
+        void setMatchPath(const std::string &path) { m_matchPath = path; }
+        void setItemAttributeName(const std::string &name);
+        void setMatchAttributeName(const std::string &name) { m_matchAttribute = name; }
+        virtual bool handleEvent(const std::string &envType, std::shared_ptr<EnvironmentNode> pEventNode);
+
+
+    protected:
+
+        std::string m_envData;
+        std::string m_matchPath;
+        std::string m_itemAttribute;
+        std::string m_matchAttribute;
+};
+
+#endif

+ 46 - 0
configuration/config2/EnvironmentLoader.hpp

@@ -0,0 +1,46 @@
+/*##############################################################################
+
+    HPCC SYSTEMS software Copyright (C) 2018 HPCC Systems®.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+############################################################################## */
+
+
+#ifndef _CONFIG2_ENVIRONMENTLOADER_HPP_
+#define _CONFIG2_ENVIRONMENTLOADER_HPP_
+
+#include <string>
+#include <fstream>
+#include <vector>
+#include "SchemaItem.hpp"
+#include "EnvironmentNode.hpp"
+#include "Status.hpp"
+#include "NameValue.hpp"
+#include "platform.h"
+
+
+class DECL_EXPORT EnvironmentLoader
+{
+    public:
+
+        EnvironmentLoader() { }
+        virtual ~EnvironmentLoader() { }
+        virtual std::vector<std::shared_ptr<EnvironmentNode>> load(std::istream &in, const std::shared_ptr<SchemaItem> &pSchemaItem) const = 0;
+
+
+    protected:
+
+        std::shared_ptr<SchemaItem> m_pSchemaItem;
+};
+
+#endif

+ 126 - 22
configuration/config2/EnvironmentMgr.cpp

@@ -18,7 +18,9 @@
 #include "EnvironmentMgr.hpp"
 #include "Exceptions.hpp"
 #include "XMLEnvironmentMgr.hpp"
+#include "InsertableItem.hpp"
 
+std::atomic_int EnvironmentMgr::m_key(1);
 
 EnvironmentMgr *getEnvironmentMgrInstance(const EnvironmentType envType)
 {
@@ -31,8 +33,7 @@ EnvironmentMgr *getEnvironmentMgrInstance(const EnvironmentType envType)
 }
 
 
-EnvironmentMgr::EnvironmentMgr() :
-    m_key(1)  // ID 0 is reserved for the root node
+EnvironmentMgr::EnvironmentMgr()
 {
     m_pSchema = std::make_shared<SchemaItem>("root");  // make the root
 }
@@ -78,7 +79,24 @@ bool EnvironmentMgr::loadEnvironment(const std::string &qualifiedFilename)
         in.open(qualifiedFilename);
         if (in.is_open())
         {
-            rc = doLoadEnvironment(in);
+            try
+            {
+                std::vector<std::shared_ptr<EnvironmentNode>> rootNodes = doLoadEnvironment(in, m_pSchema);  // root
+                if (rootNodes.size() == 1)
+                {
+                    m_pRootNode = rootNodes[0];
+                    assignNodeIds(m_pRootNode);
+                    rc = true;
+                }
+                else
+                {
+                    m_message = "There was an unknown error loading the environment";
+                }
+            }
+            catch (ParseException &pe)
+            {
+                m_message = pe.what();
+            }
         }
         else
         {
@@ -93,6 +111,17 @@ bool EnvironmentMgr::loadEnvironment(const std::string &qualifiedFilename)
 }
 
 
+std::string EnvironmentMgr::getRootNodeId() const
+{
+    std::string nodeId;
+    if (m_pRootNode != nullptr)
+    {
+        nodeId = m_pRootNode->getId();
+    }
+    return nodeId;
+}
+
+
 bool EnvironmentMgr::saveEnvironment(const std::string &qualifiedFilename)
 {
     bool rc = false;
@@ -142,56 +171,111 @@ std::shared_ptr<EnvironmentNode> EnvironmentMgr::addNewEnvironmentNode(const std
     if (pParentNode)
     {
         std::shared_ptr<SchemaItem> pNewCfgItem;
-        std::vector<std::shared_ptr<SchemaItem>> insertableItems;
+        std::vector<InsertableItem> insertableItems;
+        std::string itemType = configType;
+        std::pair<std::string, std::string> initAttributeValue;
+        size_t atPos = itemType.find_first_of('@');
+        if (atPos != std::string::npos)
+        {
+            std::string attrNameValue = itemType.substr(atPos + 1);
+            itemType.erase(atPos, std::string::npos);
+
+            size_t equalPos = attrNameValue.find_first_of('=');
+            if (equalPos != std::string::npos)
+            {
+                initAttributeValue.first = attrNameValue.substr(0, equalPos);
+                initAttributeValue.second = attrNameValue.substr(equalPos + 1);
+            }
+        }
         pParentNode->getInsertableItems(insertableItems);
         for (auto it = insertableItems.begin(); it != insertableItems.end(); ++it)
         {
-            if ((*it)->getItemType() == configType)
+            if ((*it).m_pSchemaItem->getItemType() == itemType)
             {
-                pNewNode = addNewEnvironmentNode(pParentNode, *it, status);
+                pNewNode = addNewEnvironmentNode(pParentNode, (*it).m_pSchemaItem, status, initAttributeValue);
                 break;
             }
-            if (pNewNode == nullptr)
-            {
-                status.addMsg(statusMsg::error, "Configuration type (" + configType + ") not found");
-            }
+        }
+        if (pNewNode == nullptr)
+        {
+            status.addMsg(statusMsg::error, "Configuration type (" + configType + ") not found");
         }
     }
     else
     {
         status.addMsg(statusMsg::error, parentNodeId, "", "Unable to find indicated parent node");
     }
+    pNewNode->validate(status, true, false);
     return pNewNode;
 }
 
 
-std::shared_ptr<EnvironmentNode> EnvironmentMgr::addNewEnvironmentNode(const std::shared_ptr<EnvironmentNode> &pParentNode, const std::shared_ptr<SchemaItem> &pNewCfgItem, Status &status)
+std::shared_ptr<EnvironmentNode> EnvironmentMgr::addNewEnvironmentNode(const std::shared_ptr<EnvironmentNode> &pParentNode, const std::shared_ptr<SchemaItem> &pCfgItem, Status &status,
+                                                                       const std::pair<std::string, std::string> &initAttribute)
 {
-    std::shared_ptr<EnvironmentNode> pNewNode;
+    std::shared_ptr<EnvironmentNode> pNewEnvNode;
 
     //
     // Create the new node and add it to the parent
-    pNewNode = std::make_shared<EnvironmentNode>(pNewCfgItem, pNewCfgItem->getProperty("name"), pParentNode);
-    pNewNode->setId(getUniqueKey());
-    pParentNode->addChild(pNewNode);
-    addPath(pNewNode);
-    pNewNode->initialize();
+    pNewEnvNode = std::make_shared<EnvironmentNode>(pCfgItem, pCfgItem->getProperty("name"), pParentNode);
+    pNewEnvNode->setId(EnvironmentMgr::getUniqueKey());
 
+    addPath(pNewEnvNode);
+    pNewEnvNode->initialize();
+    if (!initAttribute.first.empty())
+    {
+        std::shared_ptr<EnvironmentValue> pAttr = pNewEnvNode->getAttribute(initAttribute.first);
+        if (pAttr)
+        {
+            pAttr->setValue(initAttribute.second, nullptr);
+        }
+    }
+    pParentNode->addChild(pNewEnvNode);
+
+    //
+    // Send a create event now that it's been added to the environment
+    pCfgItem->getSchemaRoot()->processEvent("create", pNewEnvNode);
+    insertExtraEnvironmentData(m_pRootNode);
 
     //
     // Look through the children and add any that are necessary
     std::vector<std::shared_ptr<SchemaItem>> cfgChildren;
-    pNewCfgItem->getChildren(cfgChildren);
+    pCfgItem->getChildren(cfgChildren);
     for (auto childIt = cfgChildren.begin(); childIt != cfgChildren.end(); ++childIt)
     {
         int numReq = (*childIt)->getMinInstances();
-        for (int i=0; i<numReq; ++i)
+        for (int i = 0; i<numReq; ++i)
         {
-            addNewEnvironmentNode(pNewNode, *childIt, status);
+            std::pair<std::string, std::string> empty;
+            addNewEnvironmentNode(pNewEnvNode, *childIt, status, empty);
         }
     }
 
-    return pNewNode;
+    return pNewEnvNode;
+}
+
+
+void EnvironmentMgr::insertExtraEnvironmentData(std::shared_ptr<EnvironmentNode> pParentNode)
+{
+    std::string insertData = pParentNode->getEnvironmentInsertData();
+    if (!insertData.empty())
+    {
+        std::istringstream extraData(insertData);
+        std::vector<std::shared_ptr<EnvironmentNode>> extraNodes = doLoadEnvironment(extraData, pParentNode->getSchemaItem());  // not root
+        for (auto &&envNode : extraNodes)
+        {
+            assignNodeIds(envNode);
+            pParentNode->addChild(envNode);  // link extra node data to the newly created node
+            pParentNode->clearEnvironmentInsertData();
+        }
+    }
+
+    std::vector<std::shared_ptr<EnvironmentNode>> childNodes;
+    pParentNode->getChildren(childNodes);
+    for (auto &&child : childNodes)
+    {
+        insertExtraEnvironmentData(child);
+    }
 }
 
 
@@ -230,4 +314,24 @@ void EnvironmentMgr::validate(Status &status, bool includeHiddenNodes) const
     {
         status.addMsg(statusMsg::error, "No environment loaded");
     }
-}
+}
+
+
+void EnvironmentMgr::assignNodeIds(const std::shared_ptr<EnvironmentNode> &pNode)
+{
+    pNode->setId(getUniqueKey());
+    addPath(pNode);
+    std::vector<std::shared_ptr<EnvironmentNode>> children;
+    pNode->getChildren(children);
+    for (auto it=children.begin(); it!=children.end(); ++it)
+    {
+        assignNodeIds(*it);
+    }
+}
+
+
+void EnvironmentMgr::fetchNodes(const std::string path, std::vector<std::shared_ptr<EnvironmentNode>> &nodes, const std::shared_ptr<EnvironmentNode> &pStartNode) const
+{
+    const std::shared_ptr<EnvironmentNode> pStart = (pStartNode != nullptr) ? pStartNode : m_pRootNode;
+    pStart->fetchNodes(path, nodes);
+}

+ 9 - 5
configuration/config2/EnvironmentMgr.hpp

@@ -55,18 +55,22 @@ class DECL_EXPORT EnvironmentMgr
         std::shared_ptr<EnvironmentNode> addNewEnvironmentNode(const std::string &parentNodeId, const std::string &configType, Status &status);
         bool removeEnvironmentNode(const std::string &nodeId);
         bool saveEnvironment(const std::string &qualifiedFilename);
-        void discardEnvironment() { m_pRootNode = nullptr; m_nodeIds.clear(); m_key=1; }
+        void discardEnvironment() { m_pRootNode = nullptr; m_nodeIds.clear();}
         void validate(Status &status, bool includeHiddenNodes=false) const;
+        std::string getRootNodeId() const;
+        static std::string getUniqueKey();
+        void fetchNodes(const std::string path, std::vector<std::shared_ptr<EnvironmentNode>> &nodes, const std::shared_ptr<EnvironmentNode> &pStartNode = nullptr) const;
 
 
     protected:
 
-        std::string getUniqueKey();
         void addPath(const std::shared_ptr<EnvironmentNode> pNode);
         virtual bool createParser() = 0;
-        std::shared_ptr<EnvironmentNode> addNewEnvironmentNode(const std::shared_ptr<EnvironmentNode> &pParentNode, const std::shared_ptr<SchemaItem> &pNewCfgItem, Status &status);
-        virtual bool doLoadEnvironment(std::istream &in) = 0;
+        std::shared_ptr<EnvironmentNode> addNewEnvironmentNode(const std::shared_ptr<EnvironmentNode> &pParentNode, const std::shared_ptr<SchemaItem> &pNewCfgItem, Status &status, const std::pair<std::string, std::string> &initAttribute);
+        virtual std::vector<std::shared_ptr<EnvironmentNode>> doLoadEnvironment(std::istream &in, const std::shared_ptr<SchemaItem> &pSchemaItem) = 0;
         virtual bool save(std::ostream &out) = 0;
+        void assignNodeIds(const std::shared_ptr<EnvironmentNode> &pNode);
+        void insertExtraEnvironmentData(std::shared_ptr<EnvironmentNode> pNode);
 
 
     protected:
@@ -80,7 +84,7 @@ class DECL_EXPORT EnvironmentMgr
 
     private:
 
-        std::atomic_int m_key;
+        static std::atomic_int m_key;
 };
 
 #endif

+ 150 - 10
configuration/config2/EnvironmentNode.cpp

@@ -16,6 +16,8 @@
 ############################################################################## */
 
 #include "EnvironmentNode.hpp"
+#include "Exceptions.hpp"
+#include "Utils.hpp"
 
 void EnvironmentNode::addChild(std::shared_ptr<EnvironmentNode> pNode)
 {
@@ -218,7 +220,7 @@ void EnvironmentNode::validate(Status &status, bool includeChildren, bool includ
                 for (auto it = allValues.begin(); it != allValues.end() && !found; ++it)
                 {
                     auto ret = unquieValues.insert(*it);
-                    found = ret.second;
+                    found = !ret.second;
                 }
 
                 if (found)
@@ -249,7 +251,7 @@ void EnvironmentNode::validate(Status &status, bool includeChildren, bool includ
         {
             for (auto childIt = m_children.begin(); childIt != m_children.end(); ++childIt)
             {
-                childIt->second->validate(status, includeChildren);
+                childIt->second->validate(status, includeChildren, includeHiddenNodes);
             }
         }
     }
@@ -283,7 +285,7 @@ const std::shared_ptr<EnvironmentValue> EnvironmentNode::getAttribute(const std:
 }
 
 
-void EnvironmentNode::getInsertableItems(std::vector<std::shared_ptr<SchemaItem>> &insertableItems) const
+void EnvironmentNode::getInsertableItems(std::vector<InsertableItem> &insertableItems) const
 {
     std::map<std::string, unsigned> childCounts;
 
@@ -316,12 +318,12 @@ void EnvironmentNode::getInsertableItems(std::vector<std::shared_ptr<SchemaItem>
         {
             if (findIt->second < (*cfgIt)->getMaxInstances())
             {
-                insertableItems.push_back(*cfgIt);
+                insertableItems.push_back(InsertableItem(*cfgIt));
             }
         }
         else
         {
-            insertableItems.push_back(*cfgIt);
+            insertableItems.push_back(InsertableItem(*cfgIt));
         }
     }
 }
@@ -332,22 +334,160 @@ void EnvironmentNode::getInsertableItems(std::vector<std::shared_ptr<SchemaItem>
 void EnvironmentNode::initialize()
 {
     //
-    // Add any attributes that are requried
+    // Add missing attributes
     addMissingAttributesFromConfig();
 
     //
-    // If we are a comonent and there is a buildSet attribute, set the value to the configItem's type
+    // If we are a component and there is a buildSet attribute, set the value to the configItem's type
     if (!(m_pSchemaItem->getProperty("componentName").empty())  && hasAttribute("buildSet"))
     {
         Status status;
-        setAttributeValue("buildSet", m_pSchemaItem->getProperty("category"), status);
+        setAttributeValue("buildSet", m_pSchemaItem->getProperty("componentName"), status);
     }
 
-
     //
     // Initilize each attribute
     for (auto attrIt = m_attributes.begin(); attrIt != m_attributes.end(); ++attrIt)
     {
         attrIt->second->initialize();
     }
-}
+}
+
+
+void EnvironmentNode::fetchNodes(const std::string &path, std::vector<std::shared_ptr<EnvironmentNode>> &nodes) const
+{
+    //
+    // If path starts with / and we are not the root, get the root and do the fetch
+    if (path[0] == '/')
+    {
+        std::string remainingPath = path.substr(1);
+        std::string rootName = remainingPath;
+        std::shared_ptr<const EnvironmentNode> pRoot = getRoot();
+        size_t slashPos = path.find_first_of('/', 1);
+        if (slashPos != std::string::npos)
+        {
+            rootName = path.substr(1, slashPos-1);
+            remainingPath = path.substr(slashPos + 1);
+            size_t atPos = rootName.find_first_of('@');
+            if (atPos != std::string::npos)
+            {
+                rootName.erase(atPos, std::string::npos);
+            }
+        }
+
+        if (pRoot->getName() == rootName)
+        {
+            pRoot->fetchNodes(remainingPath, nodes);
+        }
+    }
+    else if (path[0] == '.')
+    {
+        //
+        // Parent ?
+        if (path[1] == '.')
+        {
+            if (!m_pParent.expired())
+            {
+                m_pParent.lock()->fetchNodes(path.substr(2), nodes);
+            }
+            else
+            {
+                throw new ParseException("Attempt to navigate to parent with no parent");
+            }
+        }
+        else
+        {
+            fetchNodes(path.substr(1), nodes); // do the fetch from here stripping the '.' indicator
+        }
+    }
+
+    //
+    // Otherwise, start searching
+    else
+    {
+        std::string nodeName = path;
+        std::string remainingPath, attributeName, attributeValue;
+
+        //
+        // Get our portion of the path which is up to the next / or the remaining string and
+        // set the remaining portion of the path to search
+        size_t slashPos = nodeName.find_first_of('/');
+        if (slashPos != std::string::npos)
+        {
+            remainingPath = nodeName.substr(slashPos + 1);
+            nodeName.erase(slashPos, std::string::npos);  // truncate
+        }
+
+        //
+        // Any attributes we need to look for?
+        size_t atPos = nodeName.find_first_of('@');
+        if (atPos != std::string::npos)
+        {
+            attributeName = nodeName.substr(atPos + 1);
+            nodeName.erase(atPos, std::string::npos);
+            size_t equalPos = attributeName.find_first_of('=');
+            if (equalPos != std::string::npos)
+            {
+                attributeValue = attributeName.substr(equalPos + 1);
+                attributeName.erase(equalPos, std::string::npos);
+            }
+        }
+
+        //
+        // Now search the children for nodes matching the node name
+        std::vector<std::shared_ptr<EnvironmentNode>> childNodes;
+        getChildren(childNodes, nodeName);
+
+        //
+        // If there is an attribute specified, dig deeper
+        if (!attributeName.empty())
+        {
+            auto childNodeIt = childNodes.begin();
+            while (childNodeIt != childNodes.end())
+            {
+                std::shared_ptr<EnvironmentValue> pValue = (*childNodeIt)->getAttribute(attributeName);
+                if (pValue)
+                {
+                    if (!attributeValue.empty() && pValue->getValue() != attributeValue)
+                    {
+                        childNodeIt = childNodes.erase(childNodeIt);
+                    }
+                    else
+                    {
+                        ++childNodeIt;
+                    }
+                }
+                else
+                {
+                    childNodeIt = childNodes.erase(childNodeIt);
+                }
+            }
+        }
+
+        //
+        // If there is path remaining, call the children in childNodes with the remaining path, otherwise we've reached
+        // the end. Whatever nodes are in childNodes are appended to the input nodes vector for return
+        if (!remainingPath.empty())
+        {
+            for (auto childNodeIt = childNodes.begin(); childNodeIt != childNodes.end(); ++childNodeIt)
+            {
+                (*childNodeIt)->fetchNodes(remainingPath, nodes);
+            }
+        }
+        else
+        {
+            nodes.insert(nodes.end(), childNodes.begin(), childNodes.end());
+        }
+    }
+}
+
+
+std::shared_ptr<const EnvironmentNode> EnvironmentNode::getRoot() const
+{
+    if (!m_pParent.expired())
+    {
+        return m_pParent.lock()->getRoot();
+    }
+    std::shared_ptr <const EnvironmentNode> ptr = shared_from_this();
+    return ptr;
+}

+ 11 - 4
configuration/config2/EnvironmentNode.hpp

@@ -24,6 +24,7 @@
 #include "EnvironmentValue.hpp"
 #include "SchemaValue.hpp"
 #include "Status.hpp"
+#include "InsertableItem.hpp"
 #include "NameValue.hpp"
 #include "platform.h"
 
@@ -38,10 +39,11 @@ class DECL_EXPORT EnvironmentNode : public std::enable_shared_from_this<Environm
         const std::string &getName() const { return m_name;  }
         void addChild(std::shared_ptr<EnvironmentNode> pNode);
         bool removeChild(std::shared_ptr<EnvironmentNode> pNode);
-        void getChildren(std::vector<std::shared_ptr<EnvironmentNode>> &children, const std::string &name="") const;
+        void getChildren(std::vector<std::shared_ptr<EnvironmentNode>> &children, const std::string &name=std::string("")) const;
         bool hasChildren() const { return m_children.size() != 0; }
         int getNumChildren() const { return m_children.size(); }
         std::shared_ptr<EnvironmentNode> getParent() const;
+        void setParent(const std::shared_ptr<EnvironmentNode> &pParent) { m_pParent = pParent; }
         bool addAttribute(const std::string &name, std::shared_ptr<EnvironmentValue> pValue);
         void setAttributeValues(const std::vector<NameValue> &values, Status &status, bool allowInvalid, bool forceCreate);
         void setAttributeValue(const std::string &name, const std::string &value, Status &status, bool allowInvalid=false, bool forceCreate=false);   // candidate for a variant?
@@ -61,8 +63,13 @@ class DECL_EXPORT EnvironmentNode : public std::enable_shared_from_this<Environm
         void validate(Status &status, bool includeChildren=false, bool includeHiddenNodes=false) const;
         void getAttributeValueForAllSiblings(const std::string &attrName, std::vector<std::string> &result) const;
         const std::shared_ptr<SchemaItem> &getSchemaItem() const { return m_pSchemaItem; }
-        void getInsertableItems(std::vector<std::shared_ptr<SchemaItem>> &items) const;
+        void getInsertableItems(std::vector<InsertableItem> &items) const;
         void initialize();
+        void fetchNodes(const std::string &path, std::vector<std::shared_ptr<EnvironmentNode>> &nodes) const;
+        std::shared_ptr<const EnvironmentNode> getRoot() const;
+        void addEnvironmentInsertData(const std::string &envData) { m_insertData = envData; }
+        const std::string &getEnvironmentInsertData() const { return m_insertData; }
+        void clearEnvironmentInsertData() { m_insertData.clear();  }
 
 
     protected:
@@ -74,7 +81,7 @@ class DECL_EXPORT EnvironmentNode : public std::enable_shared_from_this<Environm
         std::shared_ptr<EnvironmentValue> m_pLocalValue;   // not normal because values usually in attributes
         std::map<std::string, std::shared_ptr<EnvironmentValue>> m_attributes;
         std::string m_id;
+        std::string m_insertData;
 };
 
-
-#endif
+#endif

+ 14 - 5
configuration/config2/EnvironmentValue.cpp

@@ -86,6 +86,10 @@ void EnvironmentValue::validate(Status &status, const std::string &myId) const
         // Will generate status based on current value and type
         m_pSchemaValue->validate(status, myId, this);
     }
+    else if (m_pSchemaValue->isRequired())
+    {
+        status.addMsg(statusMsg::error, myId, m_name, "Required value has not been set");
+    }
 }
 
 
@@ -129,15 +133,20 @@ void EnvironmentValue::initialize()
         }
 
         //
-        // If type is configProperty, then the autogenerate value is the name of the value's node's schema item
-        // property. Set the value to this
+        // If type is configProperty, then the autogenerated value is the taken from the value of the node's indicated property
         else if (type == "configProperty")
         {
             const std::string &propertyName = m_pSchemaValue->getAutoGenerateValue();
-            std::string value;
-            value = m_pMyEnvNode.lock()->getSchemaItem()->getProperty(propertyName);
+            std::string value = m_pMyEnvNode.lock()->getSchemaItem()->getProperty(propertyName);
             setValue(value, nullptr);
         }
+
+        //
+        // Fixed value?
+        else if (type == "fixedValue")
+        {
+            setValue(m_pSchemaValue->getAutoGenerateValue(), nullptr);
+        }
     }
 }
 
@@ -145,4 +154,4 @@ void EnvironmentValue::initialize()
 std::string EnvironmentValue::getNodeId() const
 {
     return m_pMyEnvNode.lock()->getId();
-}
+}

+ 39 - 0
configuration/config2/InsertableItem.cpp

@@ -0,0 +1,39 @@
+/*##############################################################################
+
+HPCC SYSTEMS software Copyright (C) 2018 HPCC Systems®.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+############################################################################## */
+
+#include "InsertableItem.hpp"
+
+InsertableItem::InsertableItem(const std::shared_ptr<SchemaItem> &pSchemaItem) :
+    m_pSchemaItem(pSchemaItem)
+{
+
+    std::string insertLimitType = pSchemaItem->getProperty("insertLimitType");
+    if (!insertLimitType.empty())
+    {
+        if (insertLimitType == "attribute")
+        {
+            std::string attributeName = pSchemaItem->getProperty("insertLimitData");
+            std::shared_ptr<SchemaValue> pSchemaValue = pSchemaItem->getAttribute(attributeName);
+            std::vector<AllowedValue> allowedValues;
+            pSchemaValue->getAllowedValues(allowedValues);
+            for (auto &&av : allowedValues)
+            {
+                m_itemLimits.push_back(InsertItemLimitChoice(av.m_value, attributeName, av.m_value));
+            }
+        }
+    }
+}

+ 42 - 0
configuration/config2/InsertableItem.hpp

@@ -0,0 +1,42 @@
+/*##############################################################################
+
+HPCC SYSTEMS software Copyright (C) 2018 HPCC Systems®.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+############################################################################## */
+
+
+#ifndef _CONFIG2_CONFIGINSERTABLE_ITEMS_HPP_
+#define _CONFIG2_CONFIGINSERTABLE_ITEMS_HPP_
+
+#include "SchemaItem.hpp"
+#include <vector>
+
+struct InsertItemLimitChoice
+{
+    InsertItemLimitChoice(const std::string &item, const std::string &attrName, const std::string &attrVal) :
+        itemName(item), attributeName(attrName), attributeValue(attrVal) { }
+    std::string itemName;
+    std::string attributeName;
+    std::string attributeValue;
+};
+
+
+struct InsertableItem
+{
+    InsertableItem(const std::shared_ptr<SchemaItem> &pSchemaItem);
+    std::shared_ptr<SchemaItem> m_pSchemaItem;
+    std::vector<InsertItemLimitChoice> m_itemLimits;
+};
+
+#endif

+ 89 - 24
configuration/config2/SchemaItem.cpp

@@ -51,6 +51,45 @@ SchemaItem::SchemaItem(const std::string &name, const std::string &className, co
 }
 
 
+SchemaItem::SchemaItem(const SchemaItem &item)
+{
+    //
+    // Copy stuff that doesn't have to be unique
+    m_hidden = item.m_hidden;
+    m_maxInstances = item.m_maxInstances;
+    m_minInstances = item.m_minInstances;
+    //m_nodeInsertData = item.m_nodeInsertData;
+    m_properties = item.m_properties;
+    m_types = item.m_types;
+    m_schemaTypes = item.m_schemaTypes;
+    m_pParent = item.m_pParent;
+
+    if (m_pItemValue)
+        m_pItemValue = std::make_shared<SchemaValue>(*(item.m_pItemValue));  // copy constructed
+
+    //
+    // Make a copy of the children now
+    for (auto childIt = item.m_children.begin(); childIt != item.m_children.end(); ++childIt)
+    {
+        addChild(std::make_shared<SchemaItem>(*(childIt->second)));
+    }
+
+    //
+    // Copy the attributes
+    for (auto attrIt = item.m_attributes.begin(); attrIt != item.m_attributes.end(); ++attrIt)
+    {
+        addAttribute(std::make_shared<SchemaValue>(*(attrIt->second)));
+    }
+
+    //
+    // Event handlers
+    m_eventHandlers = item.m_eventHandlers;
+
+    m_uniqueAttributeValueSetReferences = item.m_uniqueAttributeValueSetReferences;
+    m_uniqueAttributeValueSetDefs = item.m_uniqueAttributeValueSetDefs;
+}
+
+
 void SchemaItem::addSchemaValueType(const std::shared_ptr<SchemaType> &pType)
 {
     m_types[pType->getName()] = pType;
@@ -140,21 +179,6 @@ std::shared_ptr<SchemaItem> SchemaItem::getSchemaType(const std::string &name, b
 void SchemaItem::insertSchemaType(const std::shared_ptr<SchemaItem> pTypeItem)
 {
     //
-    // To insert a schema type (for example a previously defined complexType name="" XSD definition)
-    // loop through each set of configurable pieces of the input type, make a copy of each, and add it to
-    // this element.
-
-    //
-    // Children
-    std::vector<std::shared_ptr<SchemaItem>> typeChildren;
-    pTypeItem->getChildren(typeChildren);
-    for (auto childIt = typeChildren.begin(); childIt != typeChildren.end(); ++childIt)
-    {
-        std::shared_ptr<SchemaItem> pNewItem = std::make_shared<SchemaItem>(*(*childIt));
-        addChild(pNewItem);
-    }
-
-    //
     // Attributes
     std::vector< std::shared_ptr<SchemaValue>> typeAttributes;
     pTypeItem->getAttributes(typeAttributes);
@@ -185,6 +209,16 @@ void SchemaItem::insertSchemaType(const std::shared_ptr<SchemaItem> pTypeItem)
     {
         m_uniqueAttributeValueSetReferences.insert({ it->first, it->second });
     }
+
+    //
+    // Children
+    std::vector<std::shared_ptr<SchemaItem>> typeChildren;
+    pTypeItem->getChildren(typeChildren);
+    for (auto childIt = typeChildren.begin(); childIt != typeChildren.end(); ++childIt)
+    {
+        std::shared_ptr<SchemaItem> pNewItem = std::make_shared<SchemaItem>(*(*childIt));
+        addChild(pNewItem);
+    }
 }
 
 
@@ -261,7 +295,7 @@ void SchemaItem::processUniqueAttributeValueSetReferences(const std::map<std::st
                 std::shared_ptr<SchemaValue> pKeyRefAttribute = *cfgIt;     // this is the reference attribute from which attributeName must be a member
                 std::string cfgValuePath = ((setRefIt->second.m_elementPath != ".") ? setRefIt->second.m_elementPath : "") + "@" + setRefIt->second.m_attributeName;
                 std::vector<std::shared_ptr<SchemaValue>> cfgValues;
-                findSchemaValues(cfgValuePath, cfgValues);
+                fetchSchemaValues(cfgValuePath, cfgValues);
                 if (!cfgValues.empty())
                 {
                     for (auto attrIt = cfgValues.begin(); attrIt != cfgValues.end(); ++attrIt)
@@ -283,7 +317,7 @@ void SchemaItem::processUniqueAttributeValueSetReferences(const std::map<std::st
 }
 
 
-void SchemaItem::getChildren(std::vector<std::shared_ptr<SchemaItem>> &children)
+void SchemaItem::getChildren(std::vector<std::shared_ptr<SchemaItem>> &children) const
 {
     for (auto it = m_children.begin(); it != m_children.end(); ++it)
     {
@@ -331,7 +365,7 @@ void SchemaItem::resetEnvironment()
 
 
 
-void SchemaItem::findSchemaValues(const std::string &path, std::vector<std::shared_ptr<SchemaValue>> &schemaValues)
+void SchemaItem::fetchSchemaValues(const std::string &path, std::vector<std::shared_ptr<SchemaValue>> &schemaValues)
 {
     bool rootPath = path[0] == '/';
 
@@ -342,7 +376,7 @@ void SchemaItem::findSchemaValues(const std::string &path, std::vector<std::shar
         std::shared_ptr<SchemaItem> pParent = m_pParent.lock();
         if (pParent)
         {
-            return pParent->findSchemaValues(path, schemaValues);
+            return pParent->fetchSchemaValues(path, schemaValues);
         }
     }
 
@@ -358,7 +392,7 @@ void SchemaItem::findSchemaValues(const std::string &path, std::vector<std::shar
         {
             if (m_properties["name"] == elem)
             {
-                return findSchemaValues(path.substr(end + 1), schemaValues);
+                return fetchSchemaValues(path.substr(end + 1), schemaValues);
             }
             else
             {
@@ -381,7 +415,7 @@ void SchemaItem::findSchemaValues(const std::string &path, std::vector<std::shar
             auto rangeIt = m_children.equal_range(elem);
             for (auto it = rangeIt.first; it != rangeIt.second; ++it)
             {
-                it->second->findSchemaValues(path.substr(end + ((path[end] == '/') ? 1 : 0)), schemaValues);
+                it->second->fetchSchemaValues(path.substr(end + ((path[end] == '/') ? 1 : 0)), schemaValues);
             }
         }
     }
@@ -390,6 +424,18 @@ void SchemaItem::findSchemaValues(const std::string &path, std::vector<std::shar
 }
 
 
+std::shared_ptr<const SchemaItem> SchemaItem::getSchemaRoot() const
+{
+    if (!m_pParent.expired())
+    {
+        return m_pParent.lock()->getSchemaRoot();
+    }
+
+    std::shared_ptr<const SchemaItem> ptr = shared_from_this();
+    return ptr;
+}
+
+
 void SchemaItem::processDefinedUniqueAttributeValueSets(std::map<std::string, std::vector<std::shared_ptr<SchemaValue>>> &uniqueAttributeValueSets)
 {
     for (auto setIt = m_uniqueAttributeValueSetDefs.begin(); setIt != m_uniqueAttributeValueSetDefs.end(); ++setIt)
@@ -400,7 +446,7 @@ void SchemaItem::processDefinedUniqueAttributeValueSets(std::map<std::string, st
         {
             std::string cfgValuePath = ((setIt->second.m_elementPath != ".") ? setIt->second.m_elementPath : "") + "@" + setIt->second.m_attributeName;
             std::vector<std::shared_ptr<SchemaValue>> cfgValues;
-            findSchemaValues(cfgValuePath, cfgValues);
+            fetchSchemaValues(cfgValuePath, cfgValues);
             if (!cfgValues.empty())
             {
                 //
@@ -450,7 +496,6 @@ void SchemaItem::processDefinedUniqueAttributeValueSets(std::map<std::string, st
 }
 
 
-
 void SchemaItem::postProcessConfig(const std::map<std::string, std::vector<std::shared_ptr<SchemaValue>>> &uniqueAttributeValueSets)
 {
     //
@@ -480,7 +525,7 @@ void SchemaItem::postProcessConfig(const std::map<std::string, std::vector<std::
         if (it->second->isMirroredValue())
         {
             std::vector<std::shared_ptr<SchemaValue>> cfgValues;
-            findSchemaValues(it->second->getMirrorFromPath(), cfgValues);
+            fetchSchemaValues(it->second->getMirrorFromPath(), cfgValues);
             if (!cfgValues.empty() && cfgValues.size() == 1)
             {
                 if (cfgValues.size() == 1)
@@ -531,3 +576,23 @@ std::string SchemaItem::getProperty(const std::string &name, const std::string &
     }
     return retVal;
 }
+
+
+void SchemaItem::processEvent(const std::string &eventType, const std::shared_ptr<EnvironmentNode> &pEventSourceNode) const
+{
+    //
+    // Loop through any event handlers we may have
+    for (auto eventIt = m_eventHandlers.begin(); eventIt != m_eventHandlers.end(); ++eventIt)
+    {
+        (*eventIt)->handleEvent(eventType, pEventSourceNode);
+    }
+
+    //
+    // Pass the event on because events are broadcast
+    std::vector<std::shared_ptr<SchemaItem>> children;
+    getChildren(children);
+    for (auto childIt = children.begin(); childIt != children.end(); ++childIt)
+    {
+        (*childIt)->processEvent(eventType, pEventSourceNode);
+    }
+}

+ 12 - 4
configuration/config2/SchemaItem.hpp

@@ -26,6 +26,9 @@
 #include "SchemaType.hpp"
 #include "SchemaValue.hpp"
 #include "platform.h"
+#include "EnvironmentEventHandlers.hpp"
+
+class EnvironmentNode;
 
 
 class DECL_EXPORT SchemaItem : public std::enable_shared_from_this<SchemaItem>
@@ -33,6 +36,7 @@ class DECL_EXPORT SchemaItem : public std::enable_shared_from_this<SchemaItem>
     public:
 
         SchemaItem(const std::string &name, const std::string &className = "category", const std::shared_ptr<SchemaItem> &pParent = nullptr);
+        SchemaItem(const SchemaItem &schemaItem);
         ~SchemaItem() { }
         std::string getItemType() const;
         void setMinInstances(unsigned num) { m_minInstances = num; }
@@ -46,13 +50,13 @@ class DECL_EXPORT SchemaItem : public std::enable_shared_from_this<SchemaItem>
         void insertSchemaType(const std::shared_ptr<SchemaItem> pTypeItem);
         void addChild(const std::shared_ptr<SchemaItem> &pItem) { m_children.insert({ pItem->getProperty("name"), pItem }); }
         void addChild(const std::shared_ptr<SchemaItem> &pItem, const std::string &name) { m_children.insert({ name, pItem }); }
-        void getChildren(std::vector<std::shared_ptr<SchemaItem>> &children);
+        void getChildren(std::vector<std::shared_ptr<SchemaItem>> &children) const;
         std::shared_ptr<SchemaItem> getChild(const std::string &name);
         std::shared_ptr<SchemaItem> getChildByComponent(const std::string &name, std::string &componentName);
         void setItemSchemaValue(const std::shared_ptr<SchemaValue> &pValue) { m_pItemValue = pValue; }
         std::shared_ptr<SchemaValue> getItemSchemaValue() const { return m_pItemValue; }
         bool isItemValueDefined() { return m_pItemValue != nullptr; }
-        void findSchemaValues(const std::string &path, std::vector<std::shared_ptr<SchemaValue>> &schemaValues);
+        void fetchSchemaValues(const std::string &path, std::vector<std::shared_ptr<SchemaValue>> &schemaValues);
         void addAttribute(const std::shared_ptr<SchemaValue> &pCfgValue);
         void addAttribute(const std::vector<std::shared_ptr<SchemaValue>> &attributes);
         void addAttribute(const std::map<std::string, std::shared_ptr<SchemaValue>> &attributes);
@@ -73,6 +77,11 @@ class DECL_EXPORT SchemaItem : public std::enable_shared_from_this<SchemaItem>
         void setHidden(bool hidden) { m_hidden = hidden; }
         bool isHidden() const { return m_hidden; }
 
+        void setParent(const std::shared_ptr<SchemaItem> &parent) { m_pParent = parent; }
+        std::shared_ptr<const SchemaItem> getSchemaRoot() const;
+        void processEvent(const std::string &eventType, const std::shared_ptr<EnvironmentNode> &pEnvNode) const;
+        void addEventHandler(const std::shared_ptr<EnvironmentEventHandler> &pHandler) { m_eventHandlers.push_back(pHandler); }
+
 
     protected:
 
@@ -108,8 +117,7 @@ class DECL_EXPORT SchemaItem : public std::enable_shared_from_this<SchemaItem>
         std::map<std::string, SetInfo> m_uniqueAttributeValueSetReferences;
         std::map<std::string, SetInfo> m_uniqueAttributeValueSetDefs;
 
-        // These are the attribute value sets whose members must be unique
-        //static std::map<std::string, std::vector<std::shared_ptr<SchemaValue>>> m_uniqueAttributeValueSets;
+        std::vector<std::shared_ptr<EnvironmentEventHandler>> m_eventHandlers;
 };
 
 #endif // _CONFIG2_CONFIGITEM_HPP_

+ 0 - 15
configuration/config2/SchemaParser.cpp

@@ -33,18 +33,3 @@ bool SchemaParser::parse(const std::string &configPath, const std::string &maste
     }
     return rc;
 }
-
-
-std::vector<std::string> SchemaParser::split(const std::string  &input, const std::string  &delim)
-{
-    size_t  start = 0, end = 0, delimLen = delim.length();
-    std::vector<std::string> list;
-
-    while (end != std::string::npos)
-    {
-        end = input.find(delim, start);
-        list.push_back(input.substr(start, (end == std::string::npos) ? std::string::npos : end - start));
-        start = ((end > (std::string::npos - delimLen)) ? std::string::npos : end + delimLen);
-    }
-    return list;
-}

+ 0 - 1
configuration/config2/SchemaParser.hpp

@@ -43,7 +43,6 @@ class DECL_EXPORT SchemaParser
 
         virtual bool doParse(const std::string &configPath, const std::string &masterConfigFile,  const std::vector<std::string> &cfgParms) = 0;
         SchemaParser() { };
-        std::vector<std::string> split(const std::string  &input, const std::string  &delim);
 
 
     protected:

+ 1 - 1
configuration/config2/SchemaTypeIntegerLimits.cpp

@@ -44,4 +44,4 @@ bool SchemaTypeIntegerLimits::doValueTest(const std::string &value) const
 std::string SchemaTypeIntegerLimits::getLimitString() const
 {
     return "integer limit string";
-}
+}

+ 20 - 1
configuration/config2/SchemaTypeLimits.cpp

@@ -46,4 +46,23 @@ bool SchemaTypeLimits::isValidEnumeratedValue(const std::string &testValue) cons
         }
     }
     return rc;
-}
+}
+
+
+void SchemaTypeLimits::addDependentAttributeValue(const std::string &value, const std::string &depAttr, const std::string &depAttrVal)
+{
+    for (auto it = m_enumeratedValues.begin(); it != m_enumeratedValues.end(); ++it)
+    {
+        if ((*it).m_value == value)
+        {
+            (*it).addDependentValue(depAttr, depAttrVal);
+            break;
+        }
+    }
+}
+
+
+void AllowedValue::addDependentValue(const std::string &attribute, const std::string &value)
+{
+    m_dependencies.push_back(DependentValue(attribute, value));
+}

+ 14 - 0
configuration/config2/SchemaTypeLimits.hpp

@@ -26,14 +26,27 @@
 
 class EnvironmentValue;
 
+
+struct DECL_EXPORT DependentValue
+{
+    DependentValue(const std::string &attribute, const std::string &value) : m_attribute(attribute), m_value(value) { }
+    std::string m_attribute;
+    std::string m_value;
+};
+
+
 struct DECL_EXPORT AllowedValue
 {
     AllowedValue(const std::string &value, const std::string &desc="") : m_value(value), m_displayName(value), m_description(desc) { }
+    void addDependentValue(const std::string &attribute, const std::string &value);
+    const std::vector<DependentValue> &getDependencies() const { return m_dependencies;  }
     std::string m_displayName;
     std::string m_value;
     std::string m_description;
+    std::vector<DependentValue> m_dependencies;
 };
 
+
 class DECL_EXPORT SchemaTypeLimits
 {
     public:
@@ -41,6 +54,7 @@ class DECL_EXPORT SchemaTypeLimits
         SchemaTypeLimits() { }
         virtual ~SchemaTypeLimits() { }
         void addAllowedValue(const std::string &value, const std::string &desc="") { m_enumeratedValues.push_back(AllowedValue(value, desc)); }
+        void addDependentAttributeValue(const std::string &value, const std::string &depAttr, const std::string &depAttrVal);
         std::vector<AllowedValue> getEnumeratedValues() const;
         bool isEnumerated() const { return !m_enumeratedValues.empty(); }
         bool isValueValid(const std::string &testValue) const;

+ 1 - 1
configuration/config2/SchemaTypeStringLimits.cpp

@@ -35,7 +35,7 @@ bool SchemaTypeStringLimits::doValueTest(const std::string &testValue) const
     // test patterns
     for (auto it = m_patterns.begin(); it != m_patterns.end() && isValid; ++it)
     {
-        std::regex expr (*it);
+        std::regex expr ((*it).c_str());
         isValid = std::regex_match(testValue, expr);
     }
     return isValid;

+ 1 - 1
configuration/config2/SchemaTypeStringLimits.hpp

@@ -53,4 +53,4 @@ class SchemaTypeStringLimits : public SchemaTypeLimits
         std::vector<std::string> m_patterns;
 };
 
-#endif // _CONFIG2_CFGSTRINGLIMITS_HPP_
+#endif // _CONFIG2_CFGSTRINGLIMITS_HPP_

+ 23 - 9
configuration/config2/SchemaValue.cpp

@@ -30,6 +30,27 @@ SchemaValue::SchemaValue(const std::string &name, bool isDefined) :
 }
 
 
+SchemaValue::SchemaValue(const SchemaValue &value)
+{
+    m_pType = value.m_pType;
+    m_name = value.m_name;
+    m_displayName = value.m_displayName;
+    m_mirrorFromPath = value.m_mirrorFromPath;
+    m_autoGenerateValue = value.m_autoGenerateValue;
+    m_autoGenerateType = value.m_autoGenerateType;
+    m_onChangeData = value.m_onChangeData;
+    m_onChangeType = value.m_onChangeType;
+    bitMask = value.bitMask;
+    m_default = value.m_default;
+    m_tooltip = value.m_tooltip;
+    m_modifiers = value.m_modifiers;
+
+    // special processing? Maybe after inserting?
+    std::vector<std::shared_ptr<SchemaValue>> m_mirrorToSchemaValues;
+    std::vector<std::weak_ptr<SchemaValue>> m_pUniqueValueSetRefs;    // this value serves as the key from which values are valid
+}
+
+
 bool SchemaValue::isValueValid(const std::string &value, const EnvironmentValue *pEnvValue) const
 {
     bool isValid = true;   // assume valid
@@ -89,13 +110,6 @@ void SchemaValue::validate(Status &status, const std::string &id, const Environm
         }
         isValid = false;
     }
-
-    // get currentvalue from pEnvValue
-    // for keyed, make sure all values are unique
-    // call pType with value to see if good
-    // call pType->limits->toString(value) if bad to get message about whats bad
-    // add to status
-
 }
 
 
@@ -146,7 +160,7 @@ void SchemaValue::getAllowedValues(std::vector<AllowedValue> &allowedValues, con
     {
         allowedValues = m_pType->getEnumeratedValues();
     }
-    else if (isFromUniqueValueSet() && pEnvValue != nullptr)
+    else if (isFromUniqueValueSet()) // && pEnvValue != nullptr)
     {
         std::vector<std::string> refValues;
         getAllKeyRefValues(refValues);
@@ -168,4 +182,4 @@ void SchemaValue::getAllKeyRefValues(std::vector<std::string> &keyRefValues) con
         pRefCfgValue->getAllEnvironmentValues(allValues);
         keyRefValues.insert(keyRefValues.end(), allValues.begin(), allValues.end());
     }
-}
+}

+ 9 - 1
configuration/config2/SchemaValue.hpp

@@ -29,6 +29,7 @@ class DECL_EXPORT SchemaValue
     public:
 
         SchemaValue(const std::string &name, bool isDefined = true);
+        SchemaValue(const SchemaValue &value);
         virtual ~SchemaValue() { }
         void setType(const std::shared_ptr<SchemaType> pType) { m_pType = pType; }
         const std::shared_ptr<SchemaType> &getType() const { return m_pType; }
@@ -75,6 +76,11 @@ class DECL_EXPORT SchemaValue
         void setAutoGenerateValue(const std::string &value) { m_autoGenerateValue = value; }
         const std::string &getAutoGenerateValue() const { return m_autoGenerateValue; }
         void getAllKeyRefValues(std::vector<std::string> &keyRefValues) const;
+        void setOnChangeType(const std::string &type) { m_onChangeType = type;  }
+        const std::string &getOnChangeType() const { return m_onChangeType;  }
+        bool isOnChangeSet() const { return !m_onChangeType.empty(); }
+        void setOnChangeData(const std::string &data) { m_onChangeData = data; }
+        const std::string &getOnChangeData() const { return m_onChangeData;  }
 
 
     protected:
@@ -87,6 +93,8 @@ class DECL_EXPORT SchemaValue
         std::string m_mirrorFromPath;
         std::string m_autoGenerateValue;
         std::string m_autoGenerateType;
+        std::string m_onChangeType;
+        std::string m_onChangeData;
 
         struct {
             unsigned m_required  : 1;
@@ -103,4 +111,4 @@ class DECL_EXPORT SchemaValue
         std::vector<std::weak_ptr<SchemaValue>> m_pUniqueValueSetRefs;    // this value serves as the key from which values are valid
 };
 
-#endif // _CONFIG2_VALUE_HPP_
+#endif // _CONFIG2_VALUE_HPP_

+ 1 - 1
configuration/config2/Status.cpp

@@ -70,4 +70,4 @@ void Status::add(const std::vector<statusMsg> msgs)
     {
         m_messages.insert({ (*msgIt).msgLevel, *msgIt });
     }
-}
+}

+ 4 - 3
configuration/config2/Status.hpp

@@ -35,14 +35,15 @@ struct DECL_EXPORT statusMsg {
         fatal
     };
 
-    statusMsg(enum msgLevel _msgLevel, const std::string &_nodeId, const std::string &_name, const std::string &_msg) :
-        msgLevel(_msgLevel), nodeId(_nodeId), attribute(_name), msg(_msg) { }
+    statusMsg(enum msgLevel _msgLevel, const std::string &_nodeId, const std::string &attrName, const std::string &_msg) :
+        msgLevel(_msgLevel), nodeId(_nodeId), attribute(attrName), msg(_msg) { }
     msgLevel msgLevel;                // Message level
     std::string nodeId;               // if not '', the node ID to which this status applies
     std::string attribute;            // possible name of attribute in nodeId
     std::string msg;                  // message for user
 };
 
+
 class DECL_EXPORT Status
 {
     public:
@@ -66,4 +67,4 @@ class DECL_EXPORT Status
         std::multimap<enum statusMsg::msgLevel, statusMsg> m_messages;
 };
 
-#endif
+#endif

+ 35 - 0
configuration/config2/Utils.cpp

@@ -0,0 +1,35 @@
+/*##############################################################################
+
+HPCC SYSTEMS software Copyright (C) 2017 HPCC Systems®.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+############################################################################## */
+
+#include <vector>
+#include <string>
+
+std::vector<std::string> splitString(const std::string  &input, const std::string  &delim)
+{
+    size_t  start = 0, end = 0, delimLen = delim.length();
+    std::vector<std::string> list;
+
+    while (end != std::string::npos)
+    {
+        end = input.find(delim, start);
+        std::string item = input.substr(start, (end == std::string::npos) ? std::string::npos : end - start);
+        if (!item.empty())
+            list.push_back(item);
+        start = ((end > (std::string::npos - delimLen)) ? std::string::npos : end + delimLen);
+    }
+    return list;
+}

+ 26 - 0
configuration/config2/Utils.hpp

@@ -0,0 +1,26 @@
+/*##############################################################################
+
+HPCC SYSTEMS software Copyright (C) 2017 HPCC Systems®.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+############################################################################## */
+
+
+#ifndef _CONFIG2_CONFIGUTILS_HPP_
+#define _CONFIG2_CONFIGUTILS_HPP_
+
+#include <vector>
+
+std::vector<std::string> splitString(const std::string  &input, const std::string  &delim);
+
+#endif

+ 126 - 0
configuration/config2/XMLEnvironmentLoader.cpp

@@ -0,0 +1,126 @@
+/*##############################################################################
+
+    HPCC SYSTEMS software Copyright (C) 2018 HPCC Systems®.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+############################################################################## */
+
+#include "XMLEnvironmentLoader.hpp"
+#include "XSDSchemaParser.hpp"
+#include "Exceptions.hpp"
+
+
+std::vector<std::shared_ptr<EnvironmentNode>> XMLEnvironmentLoader::load(std::istream &in, const std::shared_ptr<SchemaItem> &pSchemaItem) const
+{
+    std::vector<std::shared_ptr<EnvironmentNode>> envNodes;
+    std::shared_ptr<EnvironmentNode> pEnvNode;
+
+    pt::ptree envTree;
+    try
+    {
+        pt::read_xml(in, envTree, pt::xml_parser::trim_whitespace | pt::xml_parser::no_comments);
+        //auto rootIt = envTree.begin();
+
+        // if root, want to start with rootIt, but auto match rootIt first with schemaItem, then parse on down, but want to return
+        // envNode for the root
+
+        // if not root, to through rootIt with schema item as parent for each elemement, return envNode of each item iterated
+
+        std::shared_ptr<SchemaItem> pParseRootSchemaItem;
+        for (auto envIt = envTree.begin(); envIt != envTree.end(); ++envIt)
+        {
+            if (envIt->first == pSchemaItem->getProperty("name"))
+            {
+                pParseRootSchemaItem = pSchemaItem;
+            }
+            else
+            {
+                pParseRootSchemaItem = pSchemaItem->getChild(envIt->first);
+            }
+
+            pEnvNode = std::make_shared<EnvironmentNode>(pParseRootSchemaItem, envIt->first);  // caller may need to set the parent
+            parse(envIt->second, pParseRootSchemaItem, pEnvNode);
+            envNodes.push_back(pEnvNode);
+        }
+    }
+    catch (const std::exception &e)
+    {
+        std::string xmlError = e.what();
+        std::string msg = "Unable to read/parse Environment file. Error = " + xmlError;
+        throw (ParseException(msg));
+    }
+    return envNodes;
+}
+
+
+void XMLEnvironmentLoader::parse(const pt::ptree &envTree, const std::shared_ptr<SchemaItem> &pConfigItem, std::shared_ptr<EnvironmentNode> &pEnvNode) const
+{
+
+    //
+    // First see if the node has a value
+    std::string value;
+    try
+    {
+        value = envTree.get<std::string>("");
+        if (!value.empty())
+        {
+            std::shared_ptr<SchemaValue> pCfgValue = pConfigItem->getItemSchemaValue();
+            std::shared_ptr<EnvironmentValue> pEnvValue = std::make_shared<EnvironmentValue>(pEnvNode, pCfgValue, "");  // node's value has no name
+            pEnvValue->setValue(value, nullptr);
+            pEnvNode->setLocalEnvValue(pEnvValue);
+        }
+    }
+    catch (...)
+    {
+        // do nothing
+    }
+
+    //
+    // Find elements in environment tree cooresponding to this config item, then parse each
+    for (auto it = envTree.begin(); it != envTree.end(); ++it)
+    {
+        std::string elemName = it->first;
+
+        //
+        // First see if there are attributes for this element (<xmlattr> === <element attr1="xx" attr2="yy" ...></element>  The attr1 and attr2 are in this)
+        if (elemName == "<xmlattr>")
+        {
+            for (auto attrIt = it->second.begin(); attrIt != it->second.end(); ++attrIt)
+            {
+                std::shared_ptr<SchemaValue> pSchemaValue = pConfigItem->getAttribute(attrIt->first);  // note, undefined attributes in schema will return a generic schema value
+                std::string curValue = attrIt->second.get_value<std::string>();
+                std::shared_ptr<EnvironmentValue> pEnvValue = std::make_shared<EnvironmentValue>(pEnvNode, pSchemaValue, attrIt->first, curValue);   // this is where we would use a variant
+                pSchemaValue->addEnvironmentValue(pEnvValue);
+                pEnvNode->addAttribute(attrIt->first, pEnvValue);
+            }
+        }
+        else
+        {
+            std::string typeName = it->second.get("<xmlattr>.buildSet", "");
+            std::shared_ptr<SchemaItem> pSchemaItem;
+            if (!typeName.empty())
+            {
+                pSchemaItem = pConfigItem->getChildByComponent(elemName, typeName);
+            }
+            else
+            {
+                pSchemaItem = pConfigItem->getChild(elemName);
+            }
+
+            // If no schema item is found, that's ok, the node just has no defined configuration
+            std::shared_ptr<EnvironmentNode> pElementNode = std::make_shared<EnvironmentNode>(pSchemaItem, elemName, pEnvNode);
+            parse(it->second, pSchemaItem, pElementNode);
+            pEnvNode->addChild(pElementNode);
+        }
+    }
+}

+ 44 - 0
configuration/config2/XMLEnvironmentLoader.hpp

@@ -0,0 +1,44 @@
+/*##############################################################################
+
+    HPCC SYSTEMS software Copyright (C) 2018 HPCC Systems®.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+############################################################################## */
+
+
+#ifndef _CONFIG2_XMLENVIRONMENTLOADER_HPP_
+#define _CONFIG2_XMLENVIRONMENTLOADER_HPP_
+
+#include <string>
+#include <fstream>
+#include "SchemaItem.hpp"
+#include "EnvironmentNode.hpp"
+#include "EnvironmentLoader.hpp"
+
+#include <boost/property_tree/ptree.hpp>
+#include <boost/property_tree/xml_parser.hpp>
+
+namespace pt = boost::property_tree;
+
+class DECL_EXPORT XMLEnvironmentLoader : public EnvironmentLoader
+{
+    public:
+
+        XMLEnvironmentLoader() { }
+        virtual ~XMLEnvironmentLoader() { }
+        virtual std::vector<std::shared_ptr<EnvironmentNode>> load(std::istream &in, const std::shared_ptr<SchemaItem> &pSchemaItem) const override;
+        void parse(const pt::ptree &envTree, const std::shared_ptr<SchemaItem> &pConfigItem, std::shared_ptr<EnvironmentNode> &pEnvNode) const;
+
+};
+
+#endif

+ 13 - 23
configuration/config2/XMLEnvironmentMgr.cpp

@@ -17,6 +17,8 @@
 
 #include "XMLEnvironmentMgr.hpp"
 #include "XSDSchemaParser.hpp"
+#include "XMLEnvironmentLoader.hpp"
+#include "Exceptions.hpp"
 
 
 bool XMLEnvironmentMgr::createParser()
@@ -26,34 +28,21 @@ bool XMLEnvironmentMgr::createParser()
 }
 
 
-bool XMLEnvironmentMgr::doLoadEnvironment(std::istream &in)
+std::vector<std::shared_ptr<EnvironmentNode>> XMLEnvironmentMgr::doLoadEnvironment(std::istream &in, const std::shared_ptr<SchemaItem> &pSchemaItem)
 {
-
-    bool rc = true;
-    pt::ptree envTree;
+    std::vector<std::shared_ptr<EnvironmentNode>> envNodes;
     try
     {
-        pt::read_xml(in, envTree, pt::xml_parser::trim_whitespace | pt::xml_parser::no_comments);
-        auto rootIt = envTree.begin();
-
-        //
-        // Start at root, these better match!
-        std::string rootName = rootIt->first;
-        if (rootName == m_pSchema->getProperty("name"))
-        {
-            m_pRootNode = std::make_shared<EnvironmentNode>(m_pSchema, rootName);
-            m_pRootNode->setId("0");
-            addPath(m_pRootNode);
-            parse(rootIt->second, m_pSchema, m_pRootNode);
-        }
+        XMLEnvironmentLoader envLoader;
+        envNodes = envLoader.load(in, pSchemaItem);
     }
     catch (const std::exception &e)
     {
         std::string xmlError = e.what();
-        m_message = "Unable to read/parse Environment file. Error = " + xmlError;
-        rc = false;
+        std::string msg = "Unable to read/parse Environment file. Error = " + xmlError;
+        throw (ParseException(msg));
     }
-    return rc;
+    return envNodes;
 }
 
 
@@ -79,7 +68,6 @@ bool XMLEnvironmentMgr::save(std::ostream &out)
 
 void XMLEnvironmentMgr::parse(const pt::ptree &envTree, const std::shared_ptr<SchemaItem> &pConfigItem, std::shared_ptr<EnvironmentNode> &pEnvNode)
 {
-
     //
     // First see if the node has a value
     std::string value;
@@ -130,6 +118,7 @@ void XMLEnvironmentMgr::parse(const pt::ptree &envTree, const std::shared_ptr<Sc
             {
                 pSchemaItem = pConfigItem->getChild(elemName);
             }
+            // todo: need to handle pSchemaitem (remant to pChildConfigItem) not found (throw exception or make a default config item)
             std::shared_ptr<EnvironmentNode> pElementNode = std::make_shared<EnvironmentNode>(pSchemaItem, elemName, pEnvNode);
             pElementNode->setId(getUniqueKey());
             addPath(pElementNode);
@@ -146,7 +135,8 @@ void XMLEnvironmentMgr::serialize(pt::ptree &envTree, std::shared_ptr<Environmen
     pEnvNode->getAttributes(attributes);
     for (auto attrIt = attributes.begin(); attrIt != attributes.end(); ++attrIt)
     {
-        envTree.put("<xmlattr>." + (*attrIt)->getName(), (*attrIt)->getValue());
+        if ((*attrIt)->isValueSet())
+            envTree.put("<xmlattr>." + (*attrIt)->getName(), (*attrIt)->getValue());
     }
     std::shared_ptr<EnvironmentValue> pNodeValue = pEnvNode->getLocalEnvValue();
     if (pNodeValue)
@@ -161,4 +151,4 @@ void XMLEnvironmentMgr::serialize(pt::ptree &envTree, std::shared_ptr<Environmen
         serialize(nodeTree, *childIt);
         envTree.add_child((*childIt)->getName(), nodeTree);
     }
-}
+}

+ 2 - 2
configuration/config2/XMLEnvironmentMgr.hpp

@@ -36,11 +36,11 @@ class XMLEnvironmentMgr :   public EnvironmentMgr
     protected:
 
         bool createParser() override;
-        bool doLoadEnvironment(std::istream &in) override;
+        std::vector<std::shared_ptr<EnvironmentNode>> doLoadEnvironment(std::istream &in, const std::shared_ptr<SchemaItem> &pSchemaItem) override;
         bool save(std::ostream &out) override;
         void parse(const pt::ptree &envTree, const std::shared_ptr<SchemaItem> &pConfig, std::shared_ptr<EnvironmentNode> &pEnvNode);
         void serialize(pt::ptree &envTree, std::shared_ptr<EnvironmentNode> &pEnvNode) const;
 
 };
 
-#endif
+#endif

+ 188 - 84
configuration/config2/XSDSchemaParser.cpp

@@ -24,6 +24,8 @@
 #include "XSDValueSetParser.hpp"
 #include "SchemaTypeStringLimits.hpp"
 #include "SchemaTypeIntegerLimits.hpp"
+#include "EnvironmentEventHandlers.hpp"
+#include "Utils.hpp"
 
 namespace pt = boost::property_tree;
 
@@ -170,6 +172,10 @@ void XSDSchemaParser::parseXSD(const pt::ptree &keys)
         {
             parseKeyRef(it->second);
         }
+        else if (elemType == "xs:annotation")
+        {
+            parseAnnotation(it->second);
+        }
     }
 }
 
@@ -249,50 +255,22 @@ void XSDSchemaParser::parseComplexType(const pt::ptree &typeTree)
 
     if (!complexTypeName.empty())
     {
-        if (!className.empty())
+        std::shared_ptr<SchemaItem> pComplexType = std::make_shared<SchemaItem>(complexTypeName, "component", m_pSchemaItem);
+        pComplexType->setProperty("category", catName);
+        pComplexType->setProperty("componentName", componentName);
+        pComplexType->setProperty("displayName", displayName);
+        pComplexType->setHidden(hidden);
+
+        pt::ptree childTree = typeTree.get_child("", pt::ptree());
+        if (!childTree.empty())
         {
-            if (className == "component")
-            {
-                std::shared_ptr<SchemaItem> pComponent = std::make_shared<SchemaItem>(complexTypeName, "component", m_pSchemaItem);
-                pComponent->setProperty("category", catName);
-                pComponent->setProperty("componentName", componentName);
-                pComponent->setProperty("displayName", displayName);
-                pComponent->setHidden(hidden);
-                pt::ptree componentTree = typeTree.get_child("", pt::ptree());
-                if (!componentTree.empty())
-                {
-                    std::shared_ptr<XSDComponentParser> pComponentXSDParaser = std::make_shared<XSDComponentParser>(std::dynamic_pointer_cast<SchemaItem>(pComponent));
-                    pComponentXSDParaser->parseXSD(typeTree);
-                    m_pSchemaItem->addSchemaType(pComponent, complexTypeName);
-                }
-                else
-                {
-                    throw(ParseException("Component definition empty: " + displayName));
-                }
-            }
-            else
-            {
-                throw(ParseException("Unrecognized class name for complex type: " + className));
-            }
+            std::shared_ptr<XSDSchemaParser> pXSDParaser = std::make_shared<XSDSchemaParser>(pComplexType);
+            pXSDParaser->parseXSD(childTree);
+            m_pSchemaItem->addSchemaType(pComplexType, complexTypeName);
         }
-
-        //
-        // This is a complex type definition of just regular XSD statements, no special format. Create a parser and parse it
-        // and add it to the
         else
         {
-            std::shared_ptr<SchemaItem> pTypeItem = std::make_shared<SchemaItem>(complexTypeName, "", m_pSchemaItem);
-            pt::ptree childTree = typeTree.get_child("", pt::ptree());
-            if (!childTree.empty())
-            {
-                std::shared_ptr<XSDSchemaParser> pXSDParaser = std::make_shared<XSDSchemaParser>(pTypeItem);
-                pXSDParaser->parseXSD(childTree);
-                m_pSchemaItem->addSchemaType(pTypeItem, complexTypeName);
-            }
-            else
-            {
-                throw(ParseException("Complex type definition empty: " + displayName));
-            }
+            throw(ParseException("Complex type definition empty: " + displayName));
         }
     }
 
@@ -307,68 +285,77 @@ void XSDSchemaParser::parseComplexType(const pt::ptree &typeTree)
 
 void XSDSchemaParser::parseElement(const pt::ptree &elemTree)
 {
+    //
+    // Get a couple attributes to help figure out how to handle the element
     std::string elementName = elemTree.get("<xmlattr>.name", "");
-    std::string className = elemTree.get("<xmlattr>.hpcc:class", "");
     std::string category = elemTree.get("<xmlattr>.hpcc:category", "");
-    std::string displayName = elemTree.get("<xmlattr>.hpcc:displayName", "");
-    std::string tooltip = elemTree.get("<xmlattr>.hpcc:tooltip", "");
-    std::string typeName = elemTree.get("<xmlattr>.type", "");
-    unsigned minOccurs = elemTree.get("<xmlattr>.minOccurs", 1);
-    std::string maxOccursStr = elemTree.get("<xmlattr>.maxOccurs", "1");
-    unsigned maxOccurs = (maxOccursStr != "unbounded") ? stoi(maxOccursStr) : UINT_MAX;
-
-    std::shared_ptr<SchemaItem> pNewSchemaItem = std::make_shared<SchemaItem>(elementName, className, m_pSchemaItem);
-    pNewSchemaItem->setProperty("displayName", displayName);
-    pNewSchemaItem->setMinInstances(minOccurs);
-    pNewSchemaItem->setMaxInstances(maxOccurs);
-    pNewSchemaItem->setProperty("category", category);
-    pNewSchemaItem->setProperty("tooltip", tooltip);
-    pNewSchemaItem->setHidden(elemTree.get("<xmlattr>.hpcc:hidden", "false") == "true");
-
-    pt::ptree childTree = elemTree.get_child("", pt::ptree());
-
-    // special case to set the root since the top level schema can't specify it
-    if (category == "root")  // special case to set the root since the top level schema can't specify it
+
+    pt::ptree emptyTree;
+    pt::ptree childTree = elemTree.get_child("", emptyTree);
+
+    if (category == "root")
     {
         m_pSchemaItem->setProperty("name", elementName);
         parseXSD(childTree);
     }
     else
     {
+        std::string className = elemTree.get("<xmlattr>.hpcc:class", "");
+        std::string displayName = elemTree.get("<xmlattr>.hpcc:displayName", "");
+        std::string tooltip = elemTree.get("<xmlattr>.hpcc:tooltip", "");
+        std::string typeName = elemTree.get("<xmlattr>.type", "");
+        std::string componentName = elemTree.get("<xmlattr>.hpcc:componentName", "");
+        std::string itemType = elemTree.get("<xmlattr>.hpcc:itemType", "");
+        std::string insertLimitType = elemTree.get("<xmlattr>.hpcc:insertLimitType", "");
+        std::string insertLimitData = elemTree.get("<xmlattr>.hpcc:insertLimitData", "");
+        unsigned minOccurs = elemTree.get("<xmlattr>.minOccurs", 1);
+        std::string maxOccursStr = elemTree.get("<xmlattr>.maxOccurs", "1");
+        unsigned maxOccurs = (maxOccursStr != "unbounded") ? stoi(maxOccursStr) : UINT_MAX;
+        std::shared_ptr<SchemaItem> pNewSchemaItem = std::make_shared<SchemaItem>(elementName, className, m_pSchemaItem);
+
+        if (!className.empty()) pNewSchemaItem->setProperty("className", className);
+        if (!displayName.empty()) pNewSchemaItem->setProperty("displayName", displayName);
+        if (!tooltip.empty()) pNewSchemaItem->setProperty("tooltip", tooltip);
+        if (!componentName.empty()) pNewSchemaItem->setProperty("componentName", componentName);
+        if (!itemType.empty()) pNewSchemaItem->setProperty("itemType", itemType);
+        if (!insertLimitType.empty()) pNewSchemaItem->setProperty("insertLimitType", insertLimitType);
+        if (!insertLimitData.empty()) pNewSchemaItem->setProperty("insertLimitData", insertLimitData);
+        pNewSchemaItem->setMinInstances(minOccurs);
+        pNewSchemaItem->setMaxInstances(maxOccurs);
+
         //
-        // If a type is specified, then either it's a simple value type (which could be previously defined) for this element, or a named complex type.
+        // Type specified?
         if (!typeName.empty())
         {
+            //
+            // If a simple type, then it represents the element value type, so allocate a value object, set its type and add
+            // it to the item.
             const std::shared_ptr<SchemaType> pSimpleType = m_pSchemaItem->getSchemaValueType(typeName, false);
             if (pSimpleType != nullptr)
             {
                 std::shared_ptr<SchemaValue> pCfgValue = std::make_shared<SchemaValue>("");  // no name value since it's the element's value
                 pCfgValue->setType(pSimpleType);                      // will throw if type is not defined
                 pNewSchemaItem->setItemSchemaValue(pCfgValue);
+                std::shared_ptr<XSDSchemaParser> pXSDParaser = std::make_shared<XSDSchemaParser>(pNewSchemaItem);
+                pXSDParaser->parseXSD(childTree);
+                m_pSchemaItem->addChild(pNewSchemaItem);
             }
             else
             {
+                //
+                // Wasn't a simple type, complex?
                 std::shared_ptr<SchemaItem> pConfigType = m_pSchemaItem->getSchemaType(typeName, false);
                 if (pConfigType != nullptr)
                 {
                     //
-                    // Insert into this config element the component defined data (attributes, references, etc.)
-                    pNewSchemaItem->insertSchemaType(pConfigType);
-
-                    //
-                    // Set element min/max instances to that defined by the component type def (ignore values parsed above)
-                    pNewSchemaItem->setMinInstances(pConfigType->getMinInstances());
-                    pNewSchemaItem->setMaxInstances(pConfigType->getMaxInstances());
-
-                    //
-                    // If a component, then set element data (allow overriding with locally parsed values)
-                    if (pConfigType->getProperty("className") == "component")
+                    // A complex type was found. Insert it
+                    std::vector<std::shared_ptr<SchemaItem>> typeChildren;
+                    pConfigType->getChildren(typeChildren);
+                    for (auto childIt = typeChildren.begin(); childIt != typeChildren.end(); ++childIt)
                     {
-                        pNewSchemaItem->setProperty("name", (!elementName.empty()) ? elementName : pConfigType->getProperty("name"));
-                        pNewSchemaItem->setProperty("className", (!className.empty()) ? className : pConfigType->getProperty("className"));
-                        pNewSchemaItem->setProperty("category", (!category.empty()) ? category : pConfigType->getProperty("category"));
-                        pNewSchemaItem->setProperty("displayName", (!displayName.empty()) ? displayName : pConfigType->getProperty("displayName"));
-                        pNewSchemaItem->setProperty("componentName", pConfigType->getProperty("componentName"));
+                        std::shared_ptr<SchemaItem> pNewItem = std::make_shared<SchemaItem>(*(*childIt));
+                        pNewItem->setParent(m_pSchemaItem);
+                        m_pSchemaItem->addChild(pNewItem);
                     }
                 }
                 else
@@ -378,19 +365,134 @@ void XSDSchemaParser::parseElement(const pt::ptree &elemTree)
                 }
             }
         }
-
-        //
-        // Now, if there are children, create a parser and have at it
-        if (!childTree.empty())
+        else
         {
+            //
+            // No type, just continue parsing a new element
+            //pNewSchemaItem = std::make_shared<SchemaItem>(elementName, className, m_pSchemaItem);
             std::shared_ptr<XSDSchemaParser> pXSDParaser = std::make_shared<XSDSchemaParser>(pNewSchemaItem);
             pXSDParaser->parseXSD(childTree);
+            m_pSchemaItem->addChild(pNewSchemaItem);
         }
+    }
+}
+
+
+void XSDSchemaParser::parseAnnotation(const pt::ptree &elemTree)
+{
+    pt::ptree emptyTree;
+    const pt::ptree &keys = elemTree.get_child("", emptyTree);
+
+    //
+    // Parse app info sections
+    for (auto it = keys.begin(); it != keys.end(); ++it)
+    {
+        if (it->first == "xs:appinfo")
+        {
+            parseAppInfo(it->second);
+        }
+    }
+}
+
+
+
+void XSDSchemaParser::parseAppInfo(const pt::ptree &elemTree)
+{
+    std::string appInfoType = elemTree.get("<xmlattr>.hpcc:infoType", "");
+    pt::ptree emptyTree, childTree;
+
+    //
+    // Process the app info based on its type
+    if (appInfoType == "event")
+    {
+        childTree = elemTree.get_child("", emptyTree);
+        std::string eventType = getXSDAttributeValue(childTree, "eventType");
 
         //
-        // Add the element
-        m_pSchemaItem->addChild(pNewSchemaItem);
+        // Fir a create event type, get the eventAction attrbute to decide what to do
+        if (eventType == "create")
+        {
+            std::string eventAction = getXSDAttributeValue(childTree, "eventAction");
+
+            //
+            // addAttributeDependencies is used to set dependent values for an attribute based on the value of another attribute.
+            if (eventAction == "addAttributeDependencies")
+            {
+                std::shared_ptr<AttributeDependencyCreateEventHandler> pDep = std::make_shared<AttributeDependencyCreateEventHandler>();
+                pt::ptree dataTree = childTree.get_child("eventData", emptyTree);
+                for (auto it = dataTree.begin(); it != dataTree.end(); ++it)
+                {
+                    if (it->first == "itemType")
+                    {
+                        pDep->setItemType(it->second.data());
+                    }
+                    else if (it->first == "attribute")
+                    {
+                        std::string attrName = getXSDAttributeValue(it->second, "<xmlattr>.attributeName");
+                        std::string attrVal = getXSDAttributeValue(it->second, "<xmlattr>.attributeValue");
+                        std::string depAttr = getXSDAttributeValue(it->second, "<xmlattr>.dependentAttribute");
+                        std::string depVal = getXSDAttributeValue(it->second, "<xmlattr>.dependentValue");
+                        pDep->addDependency(attrName, attrVal, depAttr, depVal);
+                    }
+                }
+                m_pSchemaItem->addEventHandler(pDep);
+            }
+
+            //
+            // Insert XML is ued to insert XML ino the environment based on what's in the eventData section
+            else if (eventAction == "insertXML")
+            {
+                std::shared_ptr<InsertEnvironmentDataCreateEventHandler> pInsert = std::make_shared<InsertEnvironmentDataCreateEventHandler>();
 
+                pt::ptree dataTree = childTree.get_child("eventData", emptyTree);
+                for (auto it = dataTree.begin(); it != dataTree.end(); ++it)
+                {
+                    //
+                    // itemTye is the type of the item that was created for which the create event shall be sent.
+                    if (it->first == "itemType")
+                    {
+                        pInsert->setItemType(it->second.data());
+                    }
+
+                    //
+                    // The match section is optional. It is used to further qualify the conditions when XML is inserted. If missing,
+                    // the XML is inserted whenever a new node of "itemType" is inserted. When present, the following fields further
+                    // qualify when the XML is inserted.
+                    //
+                    //    matchItemAttribute  - name of attribute from created node whose value is compared with the value of an attribute in
+                    //                          another node (defined by matchLocalAttribte and matchPath)
+                    //    matchPath           - XPath to select the node for comparing attribute values
+                    //    matchLocalAttribute - name of attribute from node selected by matchPath for value comparison. This option is
+                    //                          optional. If not present, the name of the attribute for comparison in the selected node is
+                    //                          matchItemAttribute
+                    else if (it->first == "match")
+                    {
+                        std::string attrName = it->second.get("matchItemAttribute", "").data();
+                        pInsert->setItemAttributeName(attrName);
+                        std::string matchAttrName = it->second.get("matchLocalAttribute", "");
+                        if (!matchAttrName.empty())
+                        {
+                            pInsert->setMatchAttributeName(matchAttrName);
+                        }
+                        std::string path = it->second.get("matchPath", "");
+                        pInsert->setMatchPath(path);
+                    }
+
+                    //
+                    // the XML to be inserted. It is inserted based on the match section
+                    else if (it->first == "xml")
+                    {
+                        pt::ptree emptyTree;
+                        const pt::ptree &insertXMLTree = it->second.get_child("", emptyTree);
+
+                        std::ostringstream out;
+                        pt::write_xml(out, insertXMLTree);
+                        pInsert->setEnvironmentInsertData(out.str());
+                    }
+                }
+                m_pSchemaItem->addEventHandler(pInsert);
+            }
+        }
     }
 }
 
@@ -508,11 +610,13 @@ std::shared_ptr<SchemaValue> XSDSchemaParser::getSchemaValue(const pt::ptree &at
     pCfgValue->setAutoGenerateType(attr.get("<xmlattr>.hpcc:autoGenerateType", ""));
     pCfgValue->setAutoGenerateValue(attr.get("<xmlattr>.hpcc:autoGenerateValue", ""));
     pCfgValue->setDefaultValue(attr.get("<xmlattr>.default", ""));
+    pCfgValue->setOnChangeType(attr.get("<xmlattr>.hpcc:onChangeType", ""));
+    pCfgValue->setOnChangeData(attr.get("<xmlattr>.hpcc:onChangeData", ""));
 
     std::string modList = attr.get("<xmlattr>.hpcc:modifiers", "");
     if (modList.length())
     {
-        pCfgValue->setModifiers(split(modList, ","));
+        pCfgValue->setModifiers(splitString(modList, ","));
     }
 
     std::string typeName = attr.get("<xmlattr>.type", "");

+ 2 - 1
configuration/config2/XSDSchemaParser.hpp

@@ -53,6 +53,8 @@ class XSDSchemaParser : public SchemaParser
         virtual void parseSimpleType(const pt::ptree &typeTree);
         virtual void parseComplexType(const pt::ptree &typeTree);
         virtual void parseElement(const pt::ptree &elemTree);
+        virtual void parseAnnotation(const pt::ptree &elemTree);
+        virtual void parseAppInfo(const pt::ptree &elemTree);
 
         virtual std::shared_ptr<SchemaType> getSchemaType(const pt::ptree &typeTree, bool nameRequired=true);
         virtual std::shared_ptr<SchemaValue> getSchemaValue(const pt::ptree &attr);
@@ -67,7 +69,6 @@ class XSDSchemaParser : public SchemaParser
         std::string m_buildsetFilename;
         std::string m_basePath;
         std::string m_masterXSDFilename;
-
 };
 
 

+ 1 - 1
configuration/config2/XSDValueSetParser.cpp

@@ -49,4 +49,4 @@ void XSDValueSetParser::parseAttributeGroup(const pt::ptree &attributeTree)
         pValueSet->getAttributes(attributes);
         m_pSchemaItem->addAttribute(attributes);
     }
-}
+}

+ 34 - 10
configuration/config2/test.cpp

@@ -14,7 +14,7 @@
 
 // namespace pt = boost::property_tree;
 
-const std::string c_path = "configfiles/"; ///opt/HPCCSystems/componentfiles/config2xml/";
+const std::string c_path = "..\\..\\initfiles\\componentfiles\\config2xml\\"; ///opt/HPCCSystems/componentfiles/config2xml/";
 
 // void parseComponentXSD(pt::ptree &xsdTree);
 // void parseIncludes(pt::ptree &xsdTree);
@@ -22,6 +22,7 @@ const std::string c_path = "configfiles/"; ///opt/HPCCSystems/componentfiles/con
 #include "SchemaItem.hpp"
 #include "XSDSchemaParser.hpp"
 #include "XMLEnvironmentMgr.hpp"
+#include "InsertableItem.hpp"
 
 
 int main()
@@ -39,26 +40,49 @@ int main()
 
         //pCfgParser->parseEnvironmentConfig("newenv.xsd", "");
 
-        EnvironmentMgr *pEnvMgr = getEnvironmentMgrInstance("XML");
+        EnvironmentMgr *pEnvMgr = getEnvironmentMgrInstance(EnvironmentType::XML);
         std::vector<std::string> cfgParms;
         cfgParms.push_back("buildset.xml");  // not used right now
-        pEnvMgr->loadSchema(c_path, "newenv.xsd", cfgParms);
+        pEnvMgr->loadSchema(c_path, "environment.xsd", cfgParms);
         pEnvMgr->loadEnvironment(c_path + "/environment.xml");
 
+        std::vector<std::shared_ptr<EnvironmentNode>> nodes;
+        pEnvMgr->findNodes("/Environment/Software/EspService@buildSet=espsmc", nodes);
+
         // 158
         //auto pNode = envMgr.getNodeFromPath("158");
         // auto pNode = pEnvMgr->getEnvironmentNode("74");     // 29 is Hardware/Computer
-        auto pNode = pEnvMgr->getEnvironmentNode("35");
+        //auto pNode = pEnvMgr->getEnvironmentNode("35");
 
         //auto x = pNode->getAllFieldValues("name");
 
-        auto list = pNode->getInsertableItems();
+        //auto list = pNode->getInsertableItems();
 
         Status status;
-        auto pNewNode = pEnvMgr->addNewEnvironmentNode("35", "ws_ecl", status);
-        auto newList = pNewNode->getInsertableItems();
-        pEnvMgr->addNewEnvironmentNode("35", "ws_ecl", status);
-        pEnvMgr->addNewEnvironmentNode("35", "ws_ecl", status);
+        auto pNewNode = pEnvMgr->addNewEnvironmentNode("108", "espsmc", status);
+        //auto newList = pNewNode->getInsertableItems();
+        //pEnvMgr->addNewEnvironmentNode("35", "ws_ecl", status);
+        //pEnvMgr->addNewEnvironmentNode("35", "ws_ecl", status);
+
+        auto pNode = pEnvMgr->getEnvironmentNode("138");
+        std::vector<InsertableItem> espBindingItems;
+        pNode->getInsertableItems(espBindingItems);
+
+        /*for (auto it = espBindingItems.begin(); it != espBindingItems.end(); ++it)
+        {
+            if ((*it).m_pSchemaItem->getProperty("insertChoice") != "")
+            {
+                std::string choice = (*it).m_pSchemaItem->getProperty("insertChoice");
+                std::shared_ptr<SchemaValue> pSchemaValue = (*it).m_pSchemaItem->getAttribute(choice);
+                std::vector<AllowedValue> allowedValues;
+                pSchemaValue->getAllowedValues(allowedValues);
+            }
+        }*/
+
+
+
+        Status status2;
+        pNewNode = pEnvMgr->addNewEnvironmentNode("138", "espbinding@service=EclWatch1", status2);  // todo: when valildating, look at required flag first
 
 
         /*auto attributes = pNode->getAttributes();
@@ -91,7 +115,7 @@ int main()
 
         //pEnvMgr->saveEnvironment("testout.xml", status);
 
-        auto results = pEnvMgr->getEnvironmentNode(".");
+        //auto results = pEnvMgr->getEnvironmentNode(".");
 
     }
     catch (ParseException &e)

+ 40 - 3
esp/scm/ws_config2.ecm

@@ -20,7 +20,7 @@ ESPstruct StatusMsgType
 {
     string msgLevel;                // message level ("info", "warning", "error", "fatal")
     string nodeId;                  // node identifier that generated this status message
-    string nodeName("");            // node name (not unique, use nodeID to find node)
+    string nodeName("");            // node name (not unique, use nodeID to retrieve node)
     string attribute("");           // name of node's attribute generating the message ("" for the node itself)
     string msg;                     // the message
     ESParray<string, parentId> parentIdList;
@@ -141,7 +141,6 @@ ESPstruct NodeInfoType
     string    nodeType("");
     string    class("");
     string    category("");
-    bool      required(false);   // only used when in the insertable list
     bool      hidden(false);
     string    tooltip("");
 };
@@ -155,6 +154,26 @@ ESPstruct NodeType
 };
 
 
+ESPstruct ChoiceLimitType
+{
+    string    displayName;
+    string    itemType;
+};
+
+
+ESPstruct InsertItemType
+{
+    string    name("");
+    string    nodeType("");
+    string    class("");
+    string    category("");
+    bool      required(false);
+    string    tooltip("");
+    ESParray<ESPstruct ChoiceLimitType, Choice> choiceList;
+    bool      fixedChoices(false);
+};
+
+
 ESPstruct AttributeType
 {
     string     displayName;
@@ -182,7 +201,7 @@ ESPresponse [exceptions_inline] GetNodeResponse : StatusResponse
     ESPstruct NodeInfoType nodeInfo;
     ESParray<ESPstruct AttributeType, Attribute> attributes;
     ESParray<ESPstruct NodeType, Child> children;
-    ESParray<ESPstruct NodeInfoType, Item> insertable;           // list of insertable elements under this node
+    ESParray<ESPstruct InsertItemType, Item> insertable;         // list of insertable elements under this node
     bool localValueDefined(false);                               // true if the node is configured to have a value (not usually true)
     ESPstruct AttributeType value;                               // this would be <element>value</element> not normal, see valueDefined
 };
@@ -310,6 +329,20 @@ ESPresponse [exceptions_inline] GetTreeResponse
 };
 
 
+ESPrequest FetchNodesRequest
+{
+    string sessionId;
+    string startingNodeId("");   // optional starting node for the fetch, "" for none
+    string path;                 // path to search (uses XPath syntax). If startingNodeId specified, path may not start at root ("/")
+};
+
+
+ESPresponse [exceptions_inline] FetchNodesResponse
+{
+    ESParray<string, nodeId> nodeIds;
+};
+
+
 ESPservice [auth_feature("DEFERRED"),version("2.0"), default_client_version("2.0"), exceptions_inline("xslt/exceptions.xslt")] ws_config2
 {
     ESPMethod
@@ -376,6 +409,10 @@ ESPservice [auth_feature("DEFERRED"),version("2.0"), default_client_version("2.0
         [
             description("Return the tree of nodes beneath the indicated node for the indicated number of levels")
         ] GetNodeTree(GetTreeRequest, GetTreeResponse);
+    ESPMethod
+        [
+            description("Fetch nodes matching the indicated path")
+        ] FetchNodes(FetchNodesRequest, FetchNodesResponse);
 };
 
 

+ 1 - 0
esp/services/ws_config2/ws_config2Error.hpp

@@ -33,5 +33,6 @@
 #define CFGMGR_ERROR_ENV_EXTERNAL_CHANGE     CONFIG_MGR_ERROR_START+10   // Environment was externally modified
 #define CFGMGR_ERROR_ENVIRONMENT_LOCKING     CONFIG_MGR_ERROR_START+11   // Error locking the enironment
 #define CFGMGR_ERROR_NODE_INVALID            CONFIG_MGR_ERROR_START+12   // Enironment node not valid
+#define CDGMGR_ERROR_PATH_INVALID            CONFIG_MGR_ERROR_START+13   // The path specified is not valid
 
 #endif

+ 68 - 8
esp/services/ws_config2/ws_config2Service.cpp

@@ -18,6 +18,7 @@
 #include "ws_config2Service.hpp"
 #include "jfile.hpp"
 #include "SchemaItem.hpp"
+#include "InsertableItem.hpp"
 #include "jexcept.hpp"
 #include "ws_config2Error.hpp"
 
@@ -177,7 +178,7 @@ bool Cws_config2Ex::onOpenEnvironmentFile(IEspContext &context, IEspOpenEnvironm
         throw MakeStringException(CFGMGR_ERROR_ENVIRONMENT_NOT_LOADED, "Unable to load environment, error = %s", pSession->getLastMsg().c_str());
     }
 
-    resp.setRootNodeId("0");
+    resp.setRootNodeId(pSession->m_pEnvMgr->getRootNodeId().c_str());
 
     return true;
 }
@@ -327,6 +328,7 @@ bool Cws_config2Ex::onGetNode(IEspContext &context, IEspNodeRequest &req, IEspGe
     ConfigMgrSession *pSession = getConfigSession(sessionId);
     Status status;
 
+
     EnvironmentMgr *pEnvMgr = pSession->m_pEnvMgr;
     std::shared_ptr<EnvironmentNode> pNode = pEnvMgr->getEnvironmentNode(id);
     if (pNode == nullptr)
@@ -462,6 +464,7 @@ bool Cws_config2Ex::onGetParents(IEspContext &context, IEspNodeRequest &req, IEs
             ids.append(pNode->getId().c_str());
         }
     }
+    resp.setParentIdList(ids);
 
     return true;
 }
@@ -484,6 +487,44 @@ bool Cws_config2Ex::onGetNodeTree(IEspContext &context, IEspGetTreeRequest &req,
 }
 
 
+bool Cws_config2Ex::onFetchNodes(IEspContext &context, IEspFetchNodesRequest &req, IEspFetchNodesResponse &resp)
+{
+    std::string sessionId = req.getSessionId();
+    std::string path = req.getPath();
+    std::string startingNodeId = req.getStartingNodeId();
+    std::shared_ptr<EnvironmentNode> pStartingNode;
+    ConfigMgrSession *pSession = getConfigSession(sessionId);
+
+    if (startingNodeId != "")
+    {
+        if (path[0] == '/')
+        {
+            throw MakeStringException(CDGMGR_ERROR_PATH_INVALID, "Path may not begin at root if starting node specified");
+        }
+
+        pStartingNode = pSession->m_pEnvMgr->getEnvironmentNode(startingNodeId);
+        if (!pStartingNode)
+        {
+            throw MakeStringException(CFGMGR_ERROR_NODE_INVALID, "The starting node ID is not valid");
+        }
+    }
+    else if (path[0] != '/')
+    {
+        throw MakeStringException(CDGMGR_ERROR_PATH_INVALID, "Path must begin at root (/) if no starting node is specified");
+    }
+
+    std::vector<std::shared_ptr<EnvironmentNode>> nodes;
+    pSession->m_pEnvMgr->fetchNodes(path, nodes, pStartingNode);
+    StringArray ids;
+    for ( auto &&pNode : nodes)
+    {
+        ids.append(pNode->getId().c_str());
+    }
+    resp.setNodeIds(ids);
+
+    return true;
+}
+
 void Cws_config2Ex::addStatusToResponse(const Status &status, ConfigMgrSession *pSession, IEspStatusResponse &resp) const
 {
     std::vector<statusMsg> statusMsgs = status.getMessages();
@@ -603,16 +644,35 @@ void Cws_config2Ex::getNodeResponse(const std::shared_ptr<EnvironmentNode> &pNod
 
     //
     // Build a list of items that can be inserted under this node
-    IArrayOf<IEspNodeInfoType> newNodes;
-    std::vector<std::shared_ptr<SchemaItem>> insertableList;
+    IArrayOf<IEspInsertItemType> newNodes;
+    std::vector<InsertableItem> insertableList;
     pNode->getInsertableItems(insertableList);
     for (auto it=insertableList.begin(); it!=insertableList.end(); ++it)
     {
-        std::shared_ptr<SchemaItem> pSchemaItem = *it;
-        Owned<IEspNodeInfoType> pNodeInfo = createNodeInfoType();
-        getNodeInfo(pSchemaItem, *pNodeInfo);
-        pNodeInfo->setRequired(pSchemaItem->isRequired());   // only filled in for insertable items
-        newNodes.append(*pNodeInfo.getLink());
+        std::shared_ptr<SchemaItem> pSchemaItem = (*it).m_pSchemaItem;
+        Owned<IEspInsertItemType> pInsertInfo = createInsertItemType();
+        pInsertInfo->setName(pSchemaItem->getProperty("displayName").c_str());
+        pInsertInfo->setNodeType(pSchemaItem->getItemType().c_str());
+        pInsertInfo->setClass(pSchemaItem->getProperty("className").c_str());
+        pInsertInfo->setCategory(pSchemaItem->getProperty("category").c_str());
+        pInsertInfo->setRequired(pSchemaItem->isRequired());
+        pInsertInfo->setTooltip(pSchemaItem->getProperty("tooltip").c_str());
+        std::string limitType = pSchemaItem->getProperty("insertLimitType");
+        if (!limitType.empty())
+        {
+            pInsertInfo->setFixedChoices(true);
+            IArrayOf<IEspChoiceLimitType> fixedChoices;
+            for (auto &&fc : (*it).m_itemLimits)
+            {
+                Owned<IEspChoiceLimitType> pChoice = createChoiceLimitType();
+                pChoice->setDisplayName(fc.itemName.c_str());
+                std::string itemType = pSchemaItem->getItemType() + "@" + fc.attributeName + "=" + fc.attributeValue;
+                pChoice->setItemType(itemType.c_str());
+                fixedChoices.append(*pChoice.getLink());
+            }
+            pInsertInfo->setChoiceList(fixedChoices);
+        }
+        newNodes.append(*pInsertInfo.getLink());
     }
     resp.setInsertable(newNodes);
 

+ 1 - 0
esp/services/ws_config2/ws_config2Service.hpp

@@ -54,6 +54,7 @@ public:
     virtual bool onValidateEnvironment(IEspContext &context, IEspValidateEnvironmentRequest &req, IEspStatusResponse &resp);
     virtual bool onGetOpenSessions(IEspContext &context, IEspListOpenSessionsRequest &req, IEspListOpenSessionsResponse &resp);
     virtual bool onGetNodeTree(IEspContext &context, IEspGetTreeRequest &req, IEspGetTreeResponse &resp);
+    virtual bool onFetchNodes(IEspContext &context, IEspFetchNodesRequest &req, IEspFetchNodesResponse &resp);
 
 
 private:

+ 1 - 1
esp/services/ws_config2/ws_config2_plugin.cpp

@@ -6,7 +6,7 @@
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at
 
-        http://www.apache.org/licenses/LICENSE-2.0
+       http://www.apache.org/licenses/LICENSE-2.0
 
     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,

+ 10 - 10
initfiles/componentfiles/config2xml/environment.xsd

@@ -22,24 +22,24 @@
   <xs:include schemaLocation="types.xsd"/>
   <xs:include schemaLocation="hardware.xsd"/>
   <xs:include schemaLocation="esp.xsd"/>
-  <xs:include schemaLocation="esp_service_wsecl2.xsd"/>
+  <!--xs:include schemaLocation="esp_service_wsecl2.xsd"/-->
   <xs:include schemaLocation="esp_service_smc.xsd"/>
-  <xs:include schemaLocation="dafilesrv.xsd"/>
-  <xs:include schemaLocation="eclagent.xsd"/>
+  <!--xs:include schemaLocation="dafilesrv.xsd"/-->
+  <!--xs:include schemaLocation="eclagent.xsd"/-->
   <xs:element name="Environment" hpcc:category="root" hpcc:class="category">
     <xs:complexType>
       <xs:sequence>
-        <xs:element name="EnvSettings" hpcc:class="category" hpcc:displayName="System Settings" hpcc:hidden="true"></xs:element>
+        <!--xs:element name="EnvSettings" hpcc:class="category" hpcc:displayName="System Settings" hpcc:hidden="true"></xs:element-->
         <xs:element type="hardware" hpcc:class="category" hpcc:displayName="Hardware" hpcc:required="true"></xs:element>
-        <xs:element name="Programs" hpcc:class="category" hpcc:displayName="Programs" hpcc:hidden="true"></xs:element>
+        <!--xs:element name="Programs" hpcc:class="category" hpcc:displayName="Programs" hpcc:hidden="true"></xs:element-->
         <xs:element name="Software" hpcc:class="category" hpcc:displayName="Software">
           <xs:complexType>
             <xs:sequence>
-              <xs:element type="dafilesrv"/>  <!-- this is like an include right now -->
-              <xs:element type="eclAgent"/>
-              <xs:element type="ws_ecl"/>
+              <!--xs:element type="dafilesrv"/-->  <!-- this is like an include right now -->
+              <!--xs:element type="eclAgent"/-->
+              <!--xs:element type="ws_ecl"/-->
               <xs:element type="espsmc"/>
-              <xs:element type="espProcess"/>
+              <xs:element type="esp"/>
             </xs:sequence>
           </xs:complexType>
 
@@ -47,4 +47,4 @@
       </xs:sequence>
     </xs:complexType>
   </xs:element>
-</xs:schema>
+</xs:schema>

+ 63 - 62
initfiles/componentfiles/config2xml/esp.xsd

@@ -3,79 +3,80 @@
     xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified"
     xmlns:hpcc="someuri">
     <xs:include schemaLocation="types.xsd"/>
-    <xs:complexType name="espProcess" hpcc:class="component" hpcc:category="ESP Process" hpcc:componentName="esp" hpcc:displayName="ESP Process">
+    <xs:complexType name="esp">
         <xs:sequence>
-            <xs:element name="EspProcess" hpcc:docid="ESP.t6" maxOccurs="unbounded">
+            <xs:element name="EspProcess" hpcc:class="component" hpcc:category="ESP Process" hpcc:componentName="esp" hpcc:displayName="ESP Process"  hpcc:docid="ESP.t6" maxOccurs="unbounded">
                 <xs:annotation>
                     <xs:documentation>ESP Service Bindings</xs:documentation>
                 </xs:annotation>
                 <xs:complexType>
                     <xs:sequence>
 
-                        <xs:element name="EspBinding" maxOccurs="unbounded" hpcc:itemType="espbinding" hpcc:displayName="ESP Service Bindings" hpcc:class="valueSet">
+                        <xs:element name="EspBinding" maxOccurs="unbounded" hpcc:itemType="espbinding" hpcc:category="ESP Bindings" hpcc:insertLimitType="attribute" hpcc:insertLimitData="service" hpcc:displayName="ESP Service Bindings" hpcc:class="valueSet" hpcc:event="create">
                             <xs:complexType>
-                                <xs:element name="Authenticate" minOccurs="0" maxOccurs="unbounded" hpcc:displayName="URL Authentication" hpcc:docid="ESP.t1">
-                                    <xs:complexType>
-                                        <xs:attribute name="description" type="xs:string" use="optional" hpcc:displayName="Description"/>
-                                        <xs:attribute name="path" type="xs:string" use="required" default="/" hpcc:displayName="Path" hpcc:tooltip="The logical path of a resource used for authentication"/>
-                                        <xs:attribute name="resource" type="xs:string" use="required" hpcc:displayName="Resource" hpcc:tooltip="The physical resource for which access is checked"/>
-                                        <xs:attribute name="access" use="optional" default="Read">
-                                            <xs:simpleType>
-                                                <xs:restriction base="xs:string">
-                                                    <xs:enumeration value="" hpcc:description=""/>
-                                                    <xs:enumeration value="Access" hpcc:description=""/>
-                                                    <xs:enumeration value="Read" hpcc:description=""/>
-                                                    <xs:enumeration value="Write" hpcc:description=""/>
-                                                    <xs:enumeration value="Full" hpcc:description=""/>
-                                                    <xs:enumeration value="None" hpcc:description=""/>
-                                                </xs:restriction>
-                                            </xs:simpleType>
-                                        </xs:attribute>
-                                    </xs:complexType>
-                                </xs:element>
-                                <xs:element name="AuthenticateFeature" minOccurs="0" maxOccurs="unbounded" hpcc:displayName="Feature Authentication" hpcc:docid="ESP.t2">
-                                    <xs:complexType>
-                                        <xs:attribute name="authenticate" use="optional" default="Yes" hpcc:displayName="Authenticate" hpcc:tooltip="Validate access rights for this capability?">
-                                            <xs:simpleType>
-                                                <xs:restriction base="xs:string">
-                                                    <xs:enumeration value="Yes" hpcc:description="Validate access rights for this capability"/>
-                                                    <xs:enumeration value="No" hpcc:description="No validation"/>
-                                                </xs:restriction>
-                                            </xs:simpleType>
-                                        </xs:attribute>
-                                        <xs:attribute name="description" type="xs:string" use="optional" hpcc:displayName="Description"/>
-                                        <xs:attribute name="resource" type="xs:string" use="required" hpcc:displayName="Resource"/>
-                                    </xs:complexType>
-                                </xs:element>
-
-                                <xs:element name="CustomBindingParameter" minOccurs="0" maxOccurs="unbounded" hpcc:displayName="Custom Binding Parameters">
-                                    <xs:complexType>
-                                        <xs:attribute name="key" type="xs:string" use="optional" hpcc:displayName="Key" />
-                                        <xs:attribute name="value" type="xs:string" use="optional" hpcc:displayName="Value"/>
-                                    </xs:complexType>
-                                </xs:element>
+                                <xs:sequence>
+                                    <xs:element name="Authenticate" minOccurs="0" maxOccurs="unbounded" hpcc:displayName="URL Authentication" hpcc:docid="ESP.t1">
+                                        <xs:complexType>
+                                            <xs:attribute name="description" type="xs:string" use="optional" hpcc:displayName="Description"/>
+                                            <xs:attribute name="path" type="xs:string" use="required" default="/" hpcc:displayName="Path" hpcc:tooltip="The logical path of a resource used for authentication"/>
+                                            <xs:attribute name="resource" type="xs:string" use="required" hpcc:displayName="Resource" hpcc:tooltip="The physical resource for which access is checked"/>
+                                            <xs:attribute name="access" use="optional" default="Read">
+                                                <xs:simpleType>
+                                                    <xs:restriction base="xs:string">
+                                                        <xs:enumeration value="" hpcc:description=""/>
+                                                        <xs:enumeration value="Access" hpcc:description=""/>
+                                                        <xs:enumeration value="Read" hpcc:description=""/>
+                                                        <xs:enumeration value="Write" hpcc:description=""/>
+                                                        <xs:enumeration value="Full" hpcc:description=""/>
+                                                        <xs:enumeration value="None" hpcc:description=""/>
+                                                    </xs:restriction>
+                                                </xs:simpleType>
+                                            </xs:attribute>
+                                        </xs:complexType>
+                                    </xs:element>
+                                    <xs:element name="AuthenticateFeature" minOccurs="0" maxOccurs="unbounded" hpcc:displayName="Feature Authentication" hpcc:docid="ESP.t2">
+                                        <xs:complexType>
+                                            <xs:attribute name="authenticate" use="optional" default="Yes" hpcc:displayName="Authenticate" hpcc:tooltip="Validate access rights for this capability?">
+                                                <xs:simpleType>
+                                                    <xs:restriction base="xs:string">
+                                                        <xs:enumeration value="Yes" hpcc:description="Validate access rights for this capability"/>
+                                                        <xs:enumeration value="No" hpcc:description="No validation"/>
+                                                    </xs:restriction>
+                                                </xs:simpleType>
+                                            </xs:attribute>
+                                            <xs:attribute name="description" type="xs:string" use="optional" hpcc:displayName="Description"/>
+                                            <xs:attribute name="resource" type="xs:string" use="required" hpcc:displayName="Resource"/>
+                                        </xs:complexType>
+                                    </xs:element>
+
+                                    <xs:element name="CustomBindingParameter" minOccurs="0" maxOccurs="unbounded" hpcc:displayName="Custom Binding Parameters">
+                                        <xs:complexType>
+                                            <xs:attribute name="key" type="xs:string" use="optional" hpcc:displayName="Key" />
+                                            <xs:attribute name="value" type="xs:string" use="optional" hpcc:displayName="Value"/>
+                                        </xs:complexType>
+                                    </xs:element>
 
+                                </xs:sequence>
 
+                                <xs:attribute name="name" type="xs:string" use="required" hpcc:displayName="Binding Name" hpcc:autoName="" />
+                                <xs:attribute name="defaultServiceVersion" type="version" use="optional" hpcc:displayName="Default Service Version" hpcc:tooltip="The default version for WSDL, XSD and the ESP form"/>
+                                <xs:attribute name="defaultForPort" type="xs:boolean" use="required" default="true" hpcc:displayName="Default for port" hpcc:tooltip="This binding is determines root access"/>
+                                <xs:attribute name="port" type="xs:nonNegativeInteger" use="required" hpcc:displayName="Port" hpcc:tooltip="Port to which the service is bound"/>
+                                <xs:attribute name="protocol" use="required" default="http" hpcc:displayName="Protocol" hpcc:tooltip="The protocol to use">
+                                    <xs:simpleType>
+                                        <xs:restriction base="xs:string">
+                                            <xs:enumeration value="http" hpcc:descritpion=""/>
+                                            <xs:enumeration value="https" hpcc:descritpion=""/>
+                                        </xs:restriction>
+                                    </xs:simpleType>
+                                </xs:attribute>
+                                <xs:attribute name="resourcesBasedn" type="xs:string" use="optional" hpcc:displayName="" hpcc:tooltip="Base location for resources (used with ldap security)"/>
+                                <xs:attribute name="service" type="xs:string" use="required" hpcc:hidden="true" hpcc:displayName="Service" />
+                                <xs:attribute name="type" type="xs:string" use="optional" hpcc:displayName="Security Manager Plugin" hpcc:tooltip="The Security Manager to be used by the Esp Service"/>
+                                <xs:attribute name="workunitsBasedn" type="xs:string" use="optional" default="ou=workunits,ou=ecl" hpcc:displayName="WorkUnits BaseDn" hpcc:tooltip="Base location for workunit resources (used with ldap security)" hpcc:requiredIf="xpath,xpath..."/>
+                                <xs:attribute name="wsdlServiceAddress" type="xs:string" use="optional"hpcc:displayName="wsdlServiceAddress" hpcc:tooltip="Overrides the address used by client applications to connect to the service"/>
                             </xs:complexType>
 
-                            <xs:attribute name="name" type="xs:string" use="required" hpcc:displayName="Binding Name" hpcc:autoName="" />
-                            <xs:attribute name="defaultServiceVersion" type="version" use="optional" hpcc:displayName="Default Service Version" hpcc:tooltip="The default version for WSDL, XSD and the ESP form"/>
-                            <xs:attribute name="defaultForPort" type="xs:boolean" use="required" default="true" hpcc:displayName="Default for port" hpcc:tooltip="This binding is determines root access"/>
-                            <xs:attribute name="port" type="xs:nonNegativeInteger" use="required" hpcc:displayName="Port" hpcc:tooltip="Port to which the service is bound"/>
-                            <xs:attribute name="protocol" use="required" default="http" hpcc:displayName="Protocol" hpcc:tooltip="The protocol to use">
-                                <xs:simpleType>
-                                    <xs:restriction base="xs:string">
-                                        <xs:enumeration value="http" hpcc:descritpion=""/>
-                                        <xs:enumeration value="https" hpcc:descritpion=""/>
-                                    </xs:restriction>
-                                </xs:simpleType>
-                            </xs:attribute>
-                            <xs:attribute name="resourcesBasedn" type="xs:string" use="optional" hpcc:displayName="" hpcc:tooltip="Base location for resources (used with ldap security)"/>
-                            <xs:attribute name="service" type="xs:string" use="required" hpcc:displayName="Service"/>
-                            <xs:attribute name="type" type="xs:string" use="optional" hpcc:displayName="Security Manager Plugin" hpcc:tooltip="The Security Manager to be used by the Esp Service"/>
-                            <xs:attribute name="workunitsBasedn" type="xs:string" use="optional" default="ou=workunits,ou=ecl" hpcc:displayName="WorkUnits BaseDn" hpcc:tooltip="Base location for workunit resources (used with ldap security)" hpcc:requiredIf="xpath,xpath..."/>
-                            <xs:attribute name="wsdlServiceAddress" type="xs:string" use="optional"hpcc:displayName="wsdlServiceAddress" hpcc:tooltip="Overrides the address used by client applications to connect to the service"/>
-
                             <xs:key name="esp_custombindingparameter_key">
                                 <xs:selector xpath="CustomBindingParameter" />
                                 <xs:field xpath="@key" />
@@ -212,4 +213,4 @@
             </xs:element>
         </xs:sequence>
     </xs:complexType>
-</xs:schema>
+</xs:schema>

+ 94 - 3
initfiles/componentfiles/config2xml/esp_service_smc.xsd

@@ -3,7 +3,7 @@
     xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified"
     xmlns:hpcc="someuri">
     <xs:include schemaLocation="types.xsd"/>
-    <xs:complexType name="espsmc" hpcc:class="component" hpcc:category="espservice" hpcc:componentName="espsmc" hpcc:displayName="ESP SMC" hpcc:namePrefix="myesmsmc">
+    <xs:complexType name="espsmc">
 
         <xs:attributeGroup name="Monitoring" hpcc:docid="SMC-T02">
             <xs:attribute name="monitorDaliFileServer" type="xs:boolean" use="true" default="false" hpcc:displayName="Monior Dali File Server" hpcc:tooltip="Warn if dafilesrv process is not running on computers"/>
@@ -14,8 +14,98 @@
         </xs:attributeGroup>
 
         <xs:sequence>
-            <xs:element name="EspService" maxOccurs="unbounded" hpcc:docid="SMC-T01">
+            <xs:element name="EspService" hpcc:class="component" hpcc:componentName="espsmc" hpcc:category="ESP Service" hpcc:displayName="ESP SMC" maxOccurs="unbounded" hpcc:docid="SMC-T01">
                 <xs:complexType>
+
+                    <xs:sequence>
+                        <xs:annotation>
+                            <xs:appinfo hpcc:infoType="event">
+                                <eventType>create</eventType>
+                                <eventAction>insertXML</eventAction>
+                                <eventData>
+                                    <itemType>espsmc</itemType>
+                                    <xml>
+                                        <Properties defaultPort="8010" defaultResourcesBasedn="ou=SMC,ou=EspServices,ou=ecl" defaultSecurePort="18010" type="WsSMC"/>
+                                    </xml>
+                                </eventData>
+                            </xs:appinfo>
+
+                            <xs:appinfo hpcc:infoType="event">
+                                <eventType>create</eventType>
+                                <eventAction>insertXML</eventAction>
+                                <eventData>
+                                <itemType>espbinding</itemType>
+                                <match>
+                                    <matchItemAttribute>service</matchItemAttribute>
+                                    <matchPath>/Environment/Software/EspService</matchPath>
+                                    <matchLocalAttribute>name</matchLocalAttribute>
+                                </match>
+                                    <xml>
+                                        <Authenticate access="Read" description="Root access to SMC service" path="/" required="Read" resource="SmcAccess"/>
+                                        <AuthenticateFeature description="Access to SMC service" path="SmcAccess" resource="SmcAccess" service="ws_smc"/>
+                                        <AuthenticateFeature description="Access to thor queues" path="ThorQueueAccess" resource="ThorQueueAccess" service="ws_smc"/>
+                                        <AuthenticateFeature description="Access to roxie control commands" path="RoxieControlAccess" resource="RoxieControlAccess" service="ws_smc"/>
+                                        <AuthenticateFeature description="Access to DFU" path="DfuAccess" resource="DfuAccess" service="ws_dfu"/>
+                                        <AuthenticateFeature description="Access to DFU XRef" path="DfuXrefAccess" resource="DfuXrefAccess" service="ws_dfuxref"/>
+                                        <AuthenticateFeature description="Access to machine information" path="MachineInfoAccess" resource="MachineInfoAccess" service="ws_machine"/>
+                                        <AuthenticateFeature description="Access to SNMP metrics information" path="MetricsAccess" resource="MetricsAccess" service="ws_machine"/>
+                                        <AuthenticateFeature description="Access to DFU workunits" path="DfuWorkunitsAccess" resource="DfuWorkunitsAccess" service="ws_fs"/>
+                                        <AuthenticateFeature description="Access to DFU exceptions" path="DfuExceptionsAccess" resource="DfuExceptions" service="ws_fs"/>
+                                        <AuthenticateFeature description="Access to spraying files" path="FileSprayAccess" resource="FileSprayAccess" service="ws_fs"/>
+                                        <AuthenticateFeature description="Access to despraying of files" path="FileDesprayAccess" resource="FileDesprayAccess" service="ws_fs"/>
+                                        <AuthenticateFeature description="Access to upload files to dropzone" path="FileUploadAccess" resource="FileUploadAccess" service="ws_fs" />
+                                        <AuthenticateFeature description="Access to files in dropzone" path="FileIOAccess" resource="FileIOAccess" service="ws_fileio"/>
+                                        <AuthenticateFeature description="Access to permissions for file scopes" path="FileScopeAccess" resource="FileScopeAccess" service="ws_access"/>
+                                        <AuthenticateFeature description="Access to WS ECL service" path="WsEclAccess" resource="WsEclAccess" service="ws_ecl"/>
+                                        <AuthenticateFeature description="Access to cluster topology" path="ClusterTopologyAccess" resource="ClusterTopologyAccess" service="ws_topology"/>
+                                        <AuthenticateFeature description="Access to own workunits" path="OwnWorkunitsAccess" resource="OwnWorkunitsAccess" service="ws_workunits"/>
+                                        <AuthenticateFeature description="Access to others&apos; workunits" path="OthersWorkunitsAccess" resource="OthersWorkunitsAccess" service="ws_workunits"/>
+                                        <AuthenticateFeature description="Access to ECL direct service" path="EclDirectAccess" resource="EclDirectAccess" service="ecldirect"/>
+                                        <AuthenticateFeature description="Access to ESDL configuration service" path="ESDLConfigAccess" resource="ESDLConfigAccess" service="ws_esdlconfig"/>
+                                        <ProcessFilters>
+                                            <Platform name="Windows">
+                                                <ProcessFilter name="any">
+                                                    <Process name="dafilesrv"/>
+                                                </ProcessFilter>
+                                                <ProcessFilter multipleInstances="true" name="DfuServerProcess"/>
+                                                <ProcessFilter multipleInstances="true" name="EclCCServerProcess"/>
+                                                <ProcessFilter multipleInstances="true" name="EspProcess">
+                                                    <Process name="dafilesrv" remove="true"/>
+                                                </ProcessFilter>
+                                            </Platform>
+                                            <Platform name="Linux">
+                                                <ProcessFilter name="any">
+                                                    <Process name="dafilesrv"/>
+                                                </ProcessFilter>
+                                                <ProcessFilter multipleInstances="true" name="DfuServerProcess"/>
+                                                <ProcessFilter multipleInstances="true" name="EclCCServerProcess"/>
+                                                <ProcessFilter multipleInstances="true" name="EspProcess">
+                                                    <Process name="dafilesrv" remove="true"/>
+                                                </ProcessFilter>
+                                                <ProcessFilter name="GenesisServerProcess">
+                                                    <Process name="httpd"/>
+                                                    <Process name="atftpd"/>
+                                                    <Process name="dhcpd"/>
+                                                </ProcessFilter>
+                                            </Platform>
+                                        </ProcessFilters>
+                                    </xml>
+                                </eventData>
+                            </xs:appinfo>
+
+                            <xs:appinfo hpcc:infoType="event">
+                                <eventType>create</eventType>
+                                <eventAction>addAttributeDependencies</eventAction>
+                                <eventData>
+                                    <itemType>espbinding</itemType>
+                                    <attribute attributeName="protocol" attributeValue="http" dependentAttribute="port" dependentValue="8010"/>
+                                    <attribute attributeName="protocol" attributeValue="https" dependentAttribute="port" dependentValue="18010"/>
+                                </eventData>
+                            </xs:appinfo>
+                        </xs:annotation>
+                        <xs:element type="espservice_properties"/>
+                    </xs:sequence>
+
                     <xs:attributeGroup ref="buildInfo"/>
                     <xs:attribute name="name" type="xs:string" use="required" hpcc:displayName="Name" hpcc:autoGenerateType="prefix" hpcc:autoGenerateValue="EclWatch" hpcc:tooltip="Name for this ESP service" />
                     <xs:attribute name="description" type="xs:string" use="optional" hpcc:displayName="Description" hpcc:tooltip="Description for this process" />
@@ -33,6 +123,7 @@
                     <xs:attribute name="enableLogDaliConnection" type="xs:boolean" use="optional" default="false" hpcc:displayName="Enable Log Dali Connection" hpcc:tooltip="Enable ESP/Dali Connection ID to be logged into esp.xml" />
                     <xs:attribute name="allowNewRoxieOnDemandQuery" type="xs:boolean" use="optional" default="false" hpcc:displayName="Allow New Roxie On Demand Query" hpcc:tooltip="allow new queries to be used by roxie on demand and roxie browser" />
                     <xs:attributeGroup ref="Monitoring"/>
+
                 </xs:complexType>
 
                 <xs:key name="espservice_name_key" hpcc:allowDuplicate="true">
@@ -43,4 +134,4 @@
             </xs:element>
         </xs:sequence>
     </xs:complexType>
-</xs:schema>
+</xs:schema>

+ 3 - 1
initfiles/componentfiles/config2xml/esp_service_wsecl2.xsd

@@ -38,6 +38,8 @@
                     <xs:attribute name="workunitTimeout" type="xs:unsignedInt" use="optional" default="600" hpcc:displayName="Workunit Timeout"
                                   hpcc:tooltip="Timeout (in seconds), for WsEcl to wait for workunit to complete (0 == wait forever)" />
 
+                    <xs:complexType name="espservice_options"/>
+
                 </xs:complexType>
 
                 <xs:key name="espservice_name_key" hpcc:allowDuplicate="true">
@@ -48,4 +50,4 @@
             </xs:element>
         </xs:sequence>
     </xs:complexType>
-</xs:schema>
+</xs:schema>

+ 67 - 2
initfiles/componentfiles/config2xml/types.xsd

@@ -81,8 +81,73 @@
         <xs:attribute name="computer" type="AutoComputerType" use="optional" hpcc:displayName="Computer" hpcc:readOnly="true" hpcc:tooltip="Computer from which this note was entered"/>
         <xs:attribute name="user" type="AutoUseridType" use="optional" hpcc:displayName="User" hpcc:readOnly="true" hpcc:tooltip="User account from which this note was entered"/>
     </xs:complexType>
+
+    <xs:complexType name="espservice_properties">
+        <xs:sequence>
+            <xs:element name="Properties" use="optional" minOccurs="0" maxOccurs="1" hpcc:hidden="true">
+                <xs:complexType>
+                    <xs:sequence>
+                        <xs:element name="Authenticate" use="optional" minOccurs="0" maxOccurs="1">
+                            <xs:complexType>
+                                <xs:attribute name="access" type="xs:string"/>
+                                <xs:attribute name="description" type="xs:string"/>
+                                <xs:attribute name="path" type="xs:string"/>
+                                <xs:attribute name="required" type="xs:string"/>
+                                <xs:attribute name="resource" type="xs:string"/>
+                            </xs:complexType>
+                        </xs:element>
+                        <xs:element name="AuthenticateFeature" minOccurs="0" maxOccurs="unbounded">
+                            <xs:complexType>
+                                <xs:attribute name="description" type="xs:string"/>
+                                <xs:attribute name="path" type="xs:string"/>
+                                <xs:attribute name="resource" type="xs:string"/>
+                                <xs:attribute name="service" type="xs:string"/>
+                            </xs:complexType>
+                        </xs:element>
+                        <xs:element name="ProcessFilters" hpcc:hidden="true" minOccurs="0" maxOccurs="1">
+                            <xs:complexType>
+                                <xs:sequence>
+                                    <xs:element name="Platform" minOccurs="0" maxOccurs="unbounded">
+                                        <xs:complexType>
+                                            <xs:sequence>
+                                                <xs:element name="ProcessFilter" minOccurs="0" type="xs:string">
+                                                    <xs:complexType>
+                                                        <xs:sequence>
+                                                            <xs:element name="Process" minOccurs="0" maxOccurs="unbounded">
+                                                                <xs:complextype>
+                                                                    <xs:attribute name="name" type="xs:string" use="required"/>
+                                                                </xs:complexType>
+                                                            </xs:element>
+                                                        </xs:sequence>
+                                                        <xs:attribute name="name" type="xs:string" use="required"/>
+                                                        <xs:attribute name="multipleInstances" type="xs:boolean" default="false" use="optional"/>
+                                                    </xs:complexType>
+                                                </xs:element>
+                                            </xs:sequence>
+                                            <xs:attribute name="name" type="xs:string">
+                                                <xs:simpleType>
+                                                    <xs:restriction base="xs:string">
+                                                        <xs:enumeration value="Windows"/>
+                                                        <xs:enumeration value="Linux"/>
+                                                    </xs:restriction>
+                                                </xs:simpleType>
+                                            </xs:attribute>
+                                        </xs:complexType>
+                                    </xs:element>
+                                </xs:sequence>
+                            </xs:complexType>
+                        </xs:element>
+                    </xs:sequence>
+                    <xs:attribute name="defaultPort" type="xs:string"/>
+                    <xs:attribute name="defaultResourcesBasedn" type="xs:string"/>
+                    <xs:attribute name="defaultSecurePort" type="xs:string"/>
+                    <xs:attribute name="type" type="xs:string"/>
+                </complexType>
+            </xs:element>
+        </xs:sequence>
+    </xs:complexType>
     <xs:attributeGroup name="buildInfo">
-        <xs:attribute name="build" type="xs:string" use="required" hpcc:hidden="true" hpcc:tooltip="The build name to be deployed"/>
+        <xs:attribute name="build" type="xs:string" use="required" hpcc:hidden="true" hpcc:autoGenerateType="fixedValue" hpcc:autoGenerateValue="_"/>
         <xs:attribute name="buildSet" type="xs:string" use="required" hpcc:hidden="true" hpcc:autoGenerateType="configProperty" hpcc:autoGenerateValue="componentName"/>
     </xs:attributeGroup>
-</xs:schema>
+</xs:schema>