Genesys CTI User Forum

Genesys CTI User Forum => Genesys-related Development => Topic started by: Gabi on March 31, 2016, 01:03:09 PM

Title: How can I read agent and application configuration in WDE/IWS?
Post by: Gabi on March 31, 2016, 01:03:09 PM
I am writing a WDE extension. I have been told that I can access the application and agent configuration via the config manager (container.Resolve<IConfigManager>), but the config manager's interface is far from friendly.

Could someone please show me an example of how to get the contents of a configuration section?

Thank you in advance.
Title: Re: How can I read agent and application configuration in WDE/IWS?
Post by: Kubig on March 31, 2016, 01:26:40 PM
IConfigManager cfgMgr = container.Resolve<IConfigManager>;
cfgMgr.GetValue("name of the option on WDE object");

Do not know what is not friendly on that..
Title: Re: How can I read agent and application configuration in WDE/IWS?
Post by: Gabi on March 31, 2016, 01:32:51 PM
I would like to get the contents of an arbitrary section. Does IConfigManager only return the options of the interaction-workspace section? And is there any way to get the agent's configuration?
Title: Re: How can I read agent and application configuration in WDE/IWS?
Post by: abudwill on March 31, 2016, 01:36:52 PM
There is probably an interface in the IWS API that allows you to get the agent information, but I am not familiar with it off the top of my head.  Search through the included .chm help file for *Manager* and see if you come up with anything.

If all else fails you can use PSDK from your WDE extension, CfgPersonQuery is what you would want to look up in the PSDK documentation.
Title: Re: How can I read agent and application configuration in WDE/IWS?
Post by: Gabi on March 31, 2016, 02:55:23 PM
Perhaps there is a way to access the ConfServerProtocol?
Title: Re: How can I read agent and application configuration in WDE/IWS?
Post by: abudwill on March 31, 2016, 03:14:49 PM
There is, perhaps through IAgent / IAgentManager (I forget the name of the interface and dont have the .chm files installed right now).
Title: Re: How can I read agent and application configuration in WDE/IWS?
Post by: daniel_san on April 01, 2016, 10:47:48 AM
You can try to read some configuration directly by IAgent.

Try with IConfigurationService.

Regards.
Title: Re: How can I read agent and application configuration in WDE/IWS?
Post by: Gabi on April 05, 2016, 03:17:46 PM
Thanks, daniel_san, that looks promising. However, I'm still having some problems.

I have done the following.

[code]IConfigurationService = container.Resolve<IConfigurationService>
RequestReadObjects requestReadObjects =
                RequestReadObjects.Create(
                    (int)CfgObjectType.CFGApplication,
                    filterKey);
configurationService.ConfigServerProtocol.Request(requestReadObjects);[/code]

I have an Event Broker to handle the EventObjectsRead, which I know is deprecated but I still don't know a better way to do it, and in any case that worked when I created my own connection to the Config Server, but now...

This simply doesn't work. WDE gets stuck in some infinite loop and no log is produced.

Does anyone have any idea what I did wrong, and what should be done instead? Is it possible that the ConfigServerProtocol was not yet initialized or something?

Thanks in advance.
Title: Re: How can I read agent and application configuration in WDE/IWS?
Post by: cavagnaro on April 05, 2016, 03:34:37 PM
On CfgServer logs do you see your request?
Title: Re: How can I read agent and application configuration in WDE/IWS?
Post by: PeteHoyle on April 05, 2016, 05:36:37 PM
[quote author=Gabi link=topic=9465.msg42957#msg42957 date=1459869466]
Thanks, daniel_san, that looks promising. However, I'm still having some problems.

I have done the following.

[code]IConfigurationService = container.Resolve<IConfigurationService>
RequestReadObjects requestReadObjects =
                RequestReadObjects.Create(
                    (int)CfgObjectType.CFGApplication,
                    filterKey);
configurationService.ConfigServerProtocol.Request(requestReadObjects);[/code]

I have an Event Broker to handle the EventObjectsRead, which I know is deprecated but I still don't know a better way to do it, and in any case that worked when I created my own connection to the Config Server, but now...

This simply doesn't work. WDE gets stuck in some infinite loop and no log is produced.

Does anyone have any idea what I did wrong, and what should be done instead? Is it possible that the ConfigServerProtocol was not yet initialized or something?

Thanks in advance.
[/quote]

Which application(s) are you trying to get and which information do you need from them?
Title: Re: How can I read agent and application configuration in WDE/IWS?
Post by: Gabi on April 11, 2016, 03:03:36 PM
[quote author=PeteHoyle link=topic=9465.msg42959#msg42959 date=1459877797]
Which application(s) are you trying to get and which information do you need from them?
[/quote]I'm trying to access the WDE application and get the [b]whole[/b] configuration, not just the interaction-workspace section.

I will - eventually - need information about the current agent, agent groups and campaigns too.

I'm afraid I don't know much about reading config server logs, but here's the log of the last 2 minutes (I closed WDE right after I failed to read the configuration):

http://pastebin.com/4CSyqrN3

I think the request on line 1641 may be mine, but it may also be the one WDE does on its own when it starts.
Title: Re: How can I read agent and application configuration in WDE/IWS?
Post by: Gabi on April 11, 2016, 04:36:55 PM
Nevermind, it worked. I wasn't processing the result of the request correctly.
Title: Re: How can I read agent and application configuration in WDE/IWS?
Post by: cavagnaro on April 11, 2016, 06:56:43 PM
;D Congrats! just a suggestion, try to test always with another app which is not the one on PRD, on CME you just clone the Application you are using on PRD and use that for your tests. Easier to debug ;)

Can you please share your solution code here for reference?
Thanks and good luck!
Title: Re: How can I read agent and application configuration in WDE/IWS?
Post by: PeteHoyle on April 12, 2016, 08:22:14 AM
[quote author=Gabi link=topic=9465.msg42991#msg42991 date=1460392615]
Nevermind, it worked. I wasn't processing the result of the request correctly.
[/quote]

Looks like you got it working, another method is like this:

IConfigurationService configService = container.Resolve<IConfigurationService>();
KeyValueCollection options =  configService.MyApplication.Options;
Title: Re: How can I read agent and application configuration in WDE/IWS?
Post by: Gabi on April 21, 2016, 12:54:48 PM
[quote author=PeteHoyle link=topic=9465.msg42993#msg42993 date=1460449334]
[quote author=Gabi link=topic=9465.msg42991#msg42991 date=1460392615]
Nevermind, it worked. I wasn't processing the result of the request correctly.
[/quote]

Looks like you got it working, another method is like this:

IConfigurationService configService = container.Resolve<IConfigurationService>();
KeyValueCollection options =  configService.MyApplication.Options;
[/quote]Thanks! I've already done what I needed, but knowing about this is bound to prove useful someday.
Title: Re: How can I read agent and application configuration in WDE/IWS?
Post by: Gabi on April 21, 2016, 01:35:56 PM
[quote author=cavagnaro link=topic=9465.msg42992#msg42992 date=1460401003]
;D Congrats! just a suggestion, try to test always with another app which is not the one on PRD, on CME you just clone the Application you are using on PRD and use that for your tests. Easier to debug ;)

Can you please share your solution code here for reference?
Thanks and good luck!
[/quote]

Sorry, I don't understand. Are you suggesting that I clone the Config Server? Anyway, none of this is in production, I'm just running tests on a demo server while I develop my extension.

Here's the working code. It's a class that keeps a local cache of the application configuration options, and also provides an interface for making requests to the Configuration Server (retrieveConfigXml), which I am using to handle requests from an external application.

I'm not reading the agent configuration yet, that part is left for future work.

[code]
using System;
using System.Configuration;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.IO;
using Genesyslab.Platform.ApplicationBlocks.Commons.Broker;
using Genesyslab.Platform.Commons.Collections;
using Genesyslab.Platform.Commons.Connection;
using Genesyslab.Platform.Commons.Protocols;
using Genesyslab.Platform.Configuration.Protocols;
using Genesyslab.Platform.Configuration.Protocols.ConfServer;
using Genesyslab.Platform.Configuration.Protocols.ConfServer.Events;
using Genesyslab.Platform.Configuration.Protocols.ConfServer.Requests.Objects;
using Genesyslab.Platform.ApplicationBlocks.Commons.Protocols;
using Genesyslab.Platform.Configuration.Protocols.Types;
using System.Xml.Linq;
using System.Collections;
using Genesyslab.Platform.Commons.Logging;
using Genesyslab.Desktop.Infrastructure.DependencyInjection;
using System.Threading;
using Genesyslab.Desktop.Infrastructure.Configuration;
using Genesyslab.Desktop.Modules.Core.SDK.Configurations;
using Genesyslab.Platform.ApplicationBlocks.ConfigurationObjectModel.Queries;
using Genesyslab.Platform.ApplicationBlocks.ConfigurationObjectModel;
using Genesyslab.Desktop.Modules.Core.Model.Agents;

namespace Genesyslab.Desktop.Modules.ExtensionUtils85.Utils
{
    public class ConfigurationServiceClient : LoggingObject
    {
        protected string appName;

        protected XmlDocument doc = null;

        protected IDictionary<string, IDictionary<string, string>> config;

        private bool objectsRead = false;
       
protected ILogger log;

        protected ConfServerProtocol cfgProtocol;
       
        protected static ConfigurationServiceClient instance;

        public ConfigurationServiceClient(IObjectContainer container)
        {
            this.log = container.Resolve<ILogger>().CreateChildLogger("ConfigurationServiceClient");
            this.appName = container.Resolve<IConfigurationService>().ApplicationName;
            this.cfgProtocol = container.Resolve<IConfigurationService>().ConfigServerProtocol;
        }

        /// <summary>
        /// Initializes the service and stores the configuration options.
        /// </summary>
        /// <param name="container">WDE's object container.</param>
        public static void initialize(IObjectContainer container)
        {
            if (instance == null)
            {
                instance = new ConfigurationServiceClient(container);

                instance.ReadObjects();
            }
        }

        protected IMessage prepareRequestReadObjectsMessage(ICfgQuery query) {
            if (query is CfgXPathBasedQuery)
                return RequestReadObjects2.Create((int)(((CfgXPathBasedQuery) query).ObjectType),((CfgXPathBasedQuery) query).XPath);
            int objType = (int)CfgObjectType.CFGNoObject;
            if (query is ICfgFilterBasedQuery) {
                KeyValueCollection objectFilter = prepareReadObjectsFilter((ICfgFilterBasedQuery) query);
                objType = (int)((ICfgFilterBasedQuery) query).QueryObjectType;
                return RequestReadObjects.Create(objType, objectFilter);
            } else {
                throw new ArgumentException("Unsupported query type.");
            }
        }

protected KeyValueCollection prepareReadObjectsFilter(ICfgFilterBasedQuery query) {
KeyValueCollection objectFilter = new KeyValueCollection();
if (query != null) {
Hashtable filter = query.Filter;
foreach (string key in filter.Keys) {
Object value = filter[key];
if (value is Int64)
{
value = Convert.ToInt32(value);
}
try
{
objectFilter.Add(key, (int)value);
}
catch (Exception)
{
objectFilter.Add(key, value);
}
}
}
return objectFilter;
}

public static ConfigurationServiceClient getInstance()
{
return instance;
}



protected void OnEventError(IMessage theMessage)
{
EventError eventError = theMessage as EventError;
if (eventError == null)
{
log.Error("EventError: null.");
return;
}

log.Error("EventError: " + eventError + "\n");
}

protected void OnEventObjectsSent(IMessage theMessage)
{
EventObjectsSent objectsSent = theMessage as EventObjectsSent;
if (objectsSent == null)
{
log.Debug("EventObjectsSent: null");
return;
}

log.Debug("EventObjectsSent: "
+ objectsSent + "\n");
}

protected void OnEventObjectsRead(IMessage theMessage)
{
log.Debug("Application configuration read.");
EventObjectsRead objects =
(EventObjectsRead)theMessage;
String xml = extractXML(objects);

doc = new XmlDocument();
doc.LoadXml(xml);
log.Debug("Config XML" + xml);
this.objectsRead = true;
}

public string retrieveConfigXml(ICfgQuery query)
{
IMessage request = this.prepareRequestReadObjectsMessage(query);
ConfServerProtocol protocol = getCfgProtocol();
IMessage response = protocol.Request(request);
string xml = "";
if (response is EventObjectsRead)
{
xml = extractXML(response as EventObjectsRead);
}
else if (response is EventError)
{
OnEventError(response);
}
else
{
log.Error("Error on retrieveConfigXml. Response: " + response.ToString());
}
return xml;
}

        protected static string extractXML(EventObjectsRead objects)
        {
            XDocument resultDocument =
                (XDocument)objects.ConfObject;
            String xml = extractXML(resultDocument);
            return xml;
        }

        protected static string extractXML(XDocument document)
        {
            StringBuilder xmlAsText = new StringBuilder();
            StringWriter stringWriter = new StringWriter(xmlAsText);
            XmlTextWriter xmlTextWriter =
                new XmlTextWriter(stringWriter);
            xmlTextWriter.Formatting = Formatting.Indented;

            document.Save(xmlTextWriter);
            String xml = xmlAsText.ToString();
            return xml;
        }

        protected string getAttribute(XmlNode node, string attributeName)
        {
            string res = "null";
            if (node.Attributes[attributeName] != null)
            {
                res = node.Attributes[attributeName].InnerText;
            }
            return res;
        }

        protected string getSectionXML(string sectionName, XmlDocument doc)
        {
            string res = null;
            XmlNode node = getSectionNode(sectionName, doc);
            if (node != null)
            {
                res = node.InnerXml;
            }
            return res;
        }

        protected XmlNode getSectionNode(string sectionName, XmlDocument doc)
        {
            XmlNode res = null;
            foreach (XmlNode node in doc.DocumentElement.ChildNodes)
            {
                foreach (XmlNode innernode in node.ChildNodes)
                {
                    if ("options".Equals(innernode.Name))
                    {
                        foreach (XmlNode optionnode in innernode.ChildNodes)
                        {
                            string key = getAttribute(optionnode, "key");
                            if (key.Equals(sectionName))
                            {
                                res = optionnode;
                            }
                        }
                    }
                }
            }
            return res;
        }

        /// <summary>
        /// Gets a section from the cached application configuration.
        /// </summary>
        /// <param name="sectionName">the name of the section.</param>
        /// <returns>a dictionary with the configuration options for the section.</returns>
        public IDictionary<string, string> getSection(string sectionName)
        {
            if (doc == null)
            {
                ReadObjects();
            }
            IDictionary<string, string> dict;
            if (config.ContainsKey(sectionName))
            {
                dict = config[sectionName];
            }
            else
            {
                dict = new Dictionary<string, string>();
                XmlNode node = getSectionNode(sectionName, doc);
                if (node != null)
                {
                    foreach (XmlNode innernode in node.ChildNodes)
                    {
                        string key = getAttribute(innernode, "key");
                        string value = getAttribute(innernode, "value");
                        dict.Add(key, value);
                    }

                }
                config.Add(sectionName, dict);
            }
            return dict;
        }

       
        /// <summary>
        /// Gets the value of a configuration option from the local cache as a string.
        /// </summary>
        /// <param name="sectionName">the name of the section to read from.</param>
        /// <param name="key">the key for the desired option.</param>
        /// <param name="defaultValue">the default value in case the option is not defined.</param>
        /// <returns>the value for the option (as a string), or the default value if it is undefined.</returns>
        public string requestProperty(string sectionName, string key, string defaultValue)
        {
            if (!objectsRead)
            {
                this.ReadObjects();
            }
            IDictionary<string, string> section = getSection(sectionName);
            return requestProperty(section, key, defaultValue);
        }

        public string requestProperty(IDictionary<string, string> section, string key, string defaultValue)
        {
            string res = null;
            if (section.ContainsKey(key))
            {
                res = section[key];
            }
            if (res == null)
            {
                log.Info("Using default value " + defaultValue + " for option " + key + ".");
                res = defaultValue;
            }
            return res;
        }

       
        /// <summary>
        /// Gets the value of a configuration option from the local cache, if it is a boolean.
        /// </summary>
        /// <param name="sectionName">the name of the section to read from.</param>
        /// <param name="key">the key for the desired option.</param>
        /// <param name="defaultValue">the default value in case the option is not defined.</param>
        /// <returns>the value for the option, or the default value if it is undefined or not a boolean.</returns>
        public bool requestProperty(string sectionName, string key, bool defaultValue)
        {
            bool res = defaultValue;
            string defString = defaultValue ? "true" : "false";
            string value = this.requestProperty(sectionName, key, defString);
            string lowervalue = value.ToLower();
            if ("true".Equals(lowervalue))
            {
                res = true;
            }
            else if ("false".Equals(lowervalue))
            {
                res = false;
            }
            else
            {
                log.Warn("The value for option " + key + " should be boolean, but it is not.");
            }
            return res;
        }

        /// <summary>
        /// Gets the value of a configuration option from the local cache, if it is an integer.
        /// </summary>
        /// <param name="sectionName">the name of the section to read from.</param>
        /// <param name="key">the key for the desired option.</param>
        /// <param name="defaultValue">the default value in case the option is not defined.</param>
        /// <returns>the value for the option, or the default value if it is undefined or not an integer.</returns>
        public int requestProperty(string sectionName, string key, int defaultValue)
        {
            int res = defaultValue;
            string defString = defaultValue.ToString();
            string value = this.requestProperty(sectionName, key, defString);
            try
            {
                res = int.Parse(value);
            }
            catch (Exception ex)
            {
                log.Warn("The value for option " + key + " should be an integer, but it is not.", ex);
                res = defaultValue;
            }
            return res;
        }

        /// <summary>
        /// Updates the local application configuration cache by reading the configuration from the config server.
        /// </summary>
        public void ReadObjects()
        {
//Initialize the dictionary which will store the cached application configuration on demand.
            config = new Dictionary<string, IDictionary<string, string>>();
            KeyValueCollection filterKey = new KeyValueCollection();
            filterKey.Add("name", this.appName);
            RequestReadObjects requestReadObjects =
                RequestReadObjects.Create(
                    (int)CfgObjectType.CFGApplication,
                    filterKey);
            ConfServerProtocol protocol = getCfgProtocol();
            IMessage response = protocol.Request(requestReadObjects);
            if (response is EventObjectsRead)
            {
                OnEventObjectsRead(response);
            }
            else if (response is EventError)
            {
                OnEventError(response);
            }
            else {
                log.Error("Response: "+response.ToString());
            }
        }

        protected static ConfServerProtocol getCfgProtocol()
        {
            return this.cfgProtocol;
        }

        public string getApplicationName()
        {
            return this.appName;
        }
    }
}
[/code]