예제 #1
0
        public static void ValidateProperties(string prefix, Ice.Communicator communicator)
        {
            Dictionary <string, string> props = communicator.GetProperties(forPrefix: prefix);
            var unknownProps = new List <string>();

            foreach (string prop in props.Keys)
            {
                bool valid = false;
                foreach (string suffix in _suffixes)
                {
                    if (IceUtilInternal.StringUtil.Match(prop, prefix + suffix, false))
                    {
                        valid = true;
                        break;
                    }
                }

                if (!valid)
                {
                    unknownProps.Add(prop);
                }
            }

            if (unknownProps.Count != 0 && (communicator.GetPropertyAsBool("Ice.Warn.UnknownProperties") ?? true))
            {
                var message = new StringBuilder("found unknown IceMX properties for `");
                message.Append(prefix[0..^ 1]);
예제 #2
0
        private Dictionary <string, Regex> ParseRule(Ice.Communicator communicator, string name)
        {
            var pats = new Dictionary <string, Regex>();
            Dictionary <string, string> rules = communicator.GetProperties(forPrefix: $"{name}.");

            foreach (KeyValuePair <string, string> e in rules)
            {
                pats.Add(e.Key.Substring(name.Length + 1), new Regex(e.Value));
            }
            return(pats);
        }
예제 #3
0
    allTests(Test.TestHelper helper, int num)
    {
        var output = helper.getWriter();

        Ice.Communicator      communicator    = helper.communicator();
        List <IControllerPrx> proxies         = new List <IControllerPrx>();
        List <IControllerPrx> indirectProxies = new List <IControllerPrx>();

        for (int i = 0; i < num; ++i)
        {
            string id = "controller" + i;
            proxies.Add(IControllerPrx.Parse(id, communicator));
            indirectProxies.Add(IControllerPrx.Parse($"{id}@control{i}", communicator));
        }

        output.Write("testing indirect proxies... ");
        output.Flush();
        {
            foreach (IControllerPrx prx in indirectProxies)
            {
                prx.IcePing();
            }
        }
        output.WriteLine("ok");

        output.Write("testing well-known proxies... ");
        output.Flush();
        {
            foreach (IControllerPrx prx in proxies)
            {
                prx.IcePing();
            }
        }
        output.WriteLine("ok");

        output.Write("testing object adapter registration... ");
        output.Flush();
        {
            try
            {
                IObjectPrx.Parse("object @ oa1", communicator).IcePing();
                test(false);
            }
            catch (NoEndpointException)
            {
            }

            proxies[0].activateObjectAdapter("oa", "oa1", "");

            try
            {
                IObjectPrx.Parse("object @ oa1", communicator).IcePing();
                test(false);
            }
            catch (ObjectNotExistException)
            {
            }

            proxies[0].deactivateObjectAdapter("oa");

            try
            {
                IObjectPrx.Parse("object @ oa1", communicator).IcePing();
                test(false);
            }
            catch (NoEndpointException)
            {
            }
        }
        output.WriteLine("ok");

        output.Write("testing object adapter migration...");
        output.Flush();
        {
            proxies[0].activateObjectAdapter("oa", "oa1", "");
            proxies[0].addObject("oa", "object");
            IObjectPrx.Parse("object @ oa1", communicator).IcePing();
            proxies[0].removeObject("oa", "object");
            proxies[0].deactivateObjectAdapter("oa");

            proxies[1].activateObjectAdapter("oa", "oa1", "");
            proxies[1].addObject("oa", "object");
            IObjectPrx.Parse("object @ oa1", communicator).IcePing();
            proxies[1].removeObject("oa", "object");
            proxies[1].deactivateObjectAdapter("oa");
        }
        output.WriteLine("ok");

        output.Write("testing object migration...");
        output.Flush();
        {
            proxies[0].activateObjectAdapter("oa", "oa1", "");
            proxies[1].activateObjectAdapter("oa", "oa2", "");

            proxies[0].addObject("oa", "object");
            IObjectPrx.Parse("object @ oa1", communicator).IcePing();
            IObjectPrx.Parse("object", communicator).IcePing();
            proxies[0].removeObject("oa", "object");

            proxies[1].addObject("oa", "object");
            IObjectPrx.Parse("object @ oa2", communicator).IcePing();
            IObjectPrx.Parse("object", communicator).IcePing();
            proxies[1].removeObject("oa", "object");

            try
            {
                IObjectPrx.Parse("object @ oa1", communicator).IcePing();
            }
            catch (ObjectNotExistException)
            {
            }
            try
            {
                IObjectPrx.Parse("object @ oa2", communicator).IcePing();
            }
            catch (ObjectNotExistException)
            {
            }

            proxies[0].deactivateObjectAdapter("oa");
            proxies[1].deactivateObjectAdapter("oa");
        }
        output.WriteLine("ok");

        output.Write("testing replica groups...");
        output.Flush();
        {
            proxies[0].activateObjectAdapter("oa", "oa1", "rg");
            proxies[1].activateObjectAdapter("oa", "oa2", "rg");
            proxies[2].activateObjectAdapter("oa", "oa3", "rg");

            proxies[0].addObject("oa", "object");
            proxies[1].addObject("oa", "object");
            proxies[2].addObject("oa", "object");

            IObjectPrx.Parse("object @ oa1", communicator).IcePing();
            IObjectPrx.Parse("object @ oa2", communicator).IcePing();
            IObjectPrx.Parse("object @ oa3", communicator).IcePing();

            IObjectPrx.Parse("object @ rg", communicator).IcePing();

            List <string> adapterIds = new List <string>();
            adapterIds.Add("oa1");
            adapterIds.Add("oa2");
            adapterIds.Add("oa3");
            ITestIntfPrx intf = ITestIntfPrx.Parse("object", communicator).Clone(
                connectionCached: false,
                locatorCacheTimeout: 0);
            while (adapterIds.Count > 0)
            {
                adapterIds.Remove(intf.getAdapterId());
            }

            while (true)
            {
                adapterIds.Add("oa1");
                adapterIds.Add("oa2");
                adapterIds.Add("oa3");
                intf = ITestIntfPrx.Parse("object @ rg", communicator).Clone(connectionCached: false);
                int nRetry = 100;
                while (adapterIds.Count > 0 && --nRetry > 0)
                {
                    adapterIds.Remove(intf.getAdapterId());
                }
                if (nRetry > 0)
                {
                    break;
                }

                // The previous locator lookup probably didn't return all the replicas... try again.
                IObjectPrx.Parse("object @ rg", communicator).Clone(locatorCacheTimeout: 0).IcePing();
            }

            proxies[0].deactivateObjectAdapter("oa");
            proxies[1].deactivateObjectAdapter("oa");
            test(ITestIntfPrx.Parse("object @ rg", communicator).getAdapterId().Equals("oa3"));
            proxies[2].deactivateObjectAdapter("oa");

            proxies[0].activateObjectAdapter("oa", "oa1", "rg");
            proxies[0].addObject("oa", "object");
            test(ITestIntfPrx.Parse("object @ rg", communicator).getAdapterId().Equals("oa1"));
            proxies[0].deactivateObjectAdapter("oa");
        }
        output.WriteLine("ok");

        output.Write("testing invalid lookup endpoints... ");
        output.Flush();
        {
            string multicast;
            if (communicator.GetProperty("Ice.IPv6") == "1")
            {
                multicast = "\"ff15::1\"";
            }
            else
            {
                multicast = "239.255.0.1";
            }

            {
                var properties = communicator.GetProperties();
                properties["IceDiscovery.Lookup"] = $"udp -h {multicast} --interface unknown";
                Communicator comm = new Communicator(properties);
                test(comm.GetDefaultLocator() != null);
                try
                {
                    IObjectPrx.Parse("controller0@control0", comm).IcePing();
                    test(false);
                }
                catch (LocalException)
                {
                }
                comm.Destroy();
            }
            {
                var    properties = communicator.GetProperties();
                string intf       = communicator.GetProperty("IceDiscovery.Interface") ?? "";
                if (intf != "")
                {
                    intf = $" --interface \"{intf}\"";
                }
                string port = communicator.GetProperty("IceDiscovery.Port") ?? "";
                properties["IceDiscovery.Lookup"] =
                    $"udp -h {multicast} --interface unknown:udp -h {multicast} -p {port}{intf}";
                Communicator comm = new Communicator(properties);
                test(comm.GetDefaultLocator() != null);
                IObjectPrx.Parse("controller0@control0", comm).IcePing();
                comm.Destroy();
            }
        }
        output.WriteLine("ok");

        output.Write("shutting down... ");
        output.Flush();
        foreach (IControllerPrx prx in proxies)
        {
            prx.shutdown();
        }
        output.WriteLine("ok");
    }
예제 #4
0
파일: SSLEngine.cs 프로젝트: yssource/ice
        internal void Initialize()
        {
            if (_initialized)
            {
                return;
            }

            Ice.Communicator ic = Communicator();
            //
            // Check for a default directory. We look in this directory for
            // files mentioned in the configuration.
            //
            _defaultDir = ic.GetProperty("IceSSL.DefaultDir") ?? "";

            string        certStoreLocation = ic.GetProperty("IceSSL.CertStoreLocation") ?? "CurrentUser";
            StoreLocation storeLocation;

            if (certStoreLocation == "CurrentUser")
            {
                storeLocation = StoreLocation.CurrentUser;
            }
            else if (certStoreLocation == "LocalMachine")
            {
                storeLocation = StoreLocation.LocalMachine;
            }
            else
            {
                _logger.Warning($"Invalid IceSSL.CertStoreLocation value `{certStoreLocation}' adjusted to `CurrentUser'");
                storeLocation = StoreLocation.CurrentUser;
            }
            _useMachineContext = certStoreLocation == "LocalMachine";

            //
            // Protocols selects which protocols to enable, by default we only enable TLS1.0
            // TLS1.1 and TLS1.2 to avoid security issues with SSLv3
            //
            string[]? protocols = ic.GetPropertyAsList("IceSSL.Protocols");
            if (protocols != null)
            {
                _protocols = ParseProtocols(protocols);
            }
            else
            {
                _protocols = 0;
                foreach (int v in Enum.GetValues(typeof(SslProtocols)))
                {
                    if (v > (int)SslProtocols.Ssl3 && v != (int)SslProtocols.Default)
                    {
                        _protocols |= (SslProtocols)v;
                    }
                }
            }
            //
            // CheckCertName determines whether we compare the name in a peer's
            // certificate against its hostname.
            //
            _checkCertName = ic.GetPropertyAsInt("IceSSL.CheckCertName") > 0;

            //
            // VerifyDepthMax establishes the maximum length of a peer's certificate
            // chain, including the peer's certificate. A value of 0 means there is
            // no maximum.
            //
            _verifyDepthMax = ic.GetPropertyAsInt("IceSSL.VerifyDepthMax") ?? 3;

            //
            // CheckCRL determines whether the certificate revocation list is checked, and how strictly.
            //
            _checkCRL = ic.GetPropertyAsInt("IceSSL.CheckCRL") ?? 0;

            //
            // Check for a certificate verifier.
            //
            string?certVerifierClass = ic.GetProperty("IceSSL.CertVerifier");

            if (certVerifierClass != null)
            {
                if (_verifier != null)
                {
                    throw new InvalidOperationException("IceSSL: certificate verifier already installed");
                }

                Type?cls = _facade.FindType(certVerifierClass);
                if (cls == null)
                {
                    throw new InvalidConfigurationException(
                              $"IceSSL: unable to load certificate verifier class `{certVerifierClass}'");
                }

                try
                {
                    _verifier = (ICertificateVerifier)IceInternal.AssemblyUtil.CreateInstance(cls);
                }
                catch (Exception ex)
                {
                    throw new LoadException(
                              $"IceSSL: unable to instantiate certificate verifier class `{certVerifierClass}", ex);
                }
            }

            //
            // Check for a password callback.
            //
            string?passwordCallbackClass = ic.GetProperty("IceSSL.PasswordCallback");

            if (passwordCallbackClass != null)
            {
                if (_passwordCallback != null)
                {
                    throw new InvalidOperationException("IceSSL: password callback already installed");
                }

                Type?cls = _facade.FindType(passwordCallbackClass);
                if (cls == null)
                {
                    throw new InvalidConfigurationException(
                              $"IceSSL: unable to load password callback class `{passwordCallbackClass}'");
                }

                try
                {
                    _passwordCallback = (IPasswordCallback)IceInternal.AssemblyUtil.CreateInstance(cls);
                }
                catch (Exception ex)
                {
                    throw new LoadException(
                              $"IceSSL: unable to load password callback class {passwordCallbackClass}", ex);
                }
            }

            //
            // If the user hasn't supplied a certificate collection, we need to examine
            // the property settings.
            //
            if (_certs == null)
            {
                //
                // If IceSSL.CertFile is defined, load a certificate from a file and
                // add it to the collection.
                //
                // TODO: tracing?
                _certs = new X509Certificate2Collection();
                string?      certFile    = ic.GetProperty("IceSSL.CertFile");
                string?      passwordStr = ic.GetProperty("IceSSL.Password");
                string?      findCert    = ic.GetProperty("IceSSL.FindCert");
                const string findPrefix  = "IceSSL.FindCert.";
                Dictionary <string, string> findCertProps = ic.GetProperties(forPrefix: findPrefix);

                if (certFile != null)
                {
                    if (!CheckPath(ref certFile))
                    {
                        throw new FileNotFoundException($"IceSSL: certificate file not found: `{certFile}'", certFile);
                    }

                    SecureString?password = null;
                    if (passwordStr != null)
                    {
                        password = CreateSecureString(passwordStr);
                    }
                    else if (_passwordCallback != null)
                    {
                        password = _passwordCallback.GetPassword(certFile);
                    }

                    try
                    {
                        X509Certificate2    cert;
                        X509KeyStorageFlags importFlags;
                        if (_useMachineContext)
                        {
                            importFlags = X509KeyStorageFlags.MachineKeySet;
                        }
                        else
                        {
                            importFlags = X509KeyStorageFlags.UserKeySet;
                        }

                        if (password != null)
                        {
                            cert = new X509Certificate2(certFile, password, importFlags);
                        }
                        else
                        {
                            cert = new X509Certificate2(certFile, "", importFlags);
                        }
                        _certs.Add(cert);
                    }
                    catch (CryptographicException ex)
                    {
                        throw new InvalidConfigurationException(
                                  $"IceSSL: error while attempting to load certificate from `{certFile}'", ex);
                    }
                }
                else if (findCert != null)
                {
                    string certStore = ic.GetProperty("IceSSL.CertStore") ?? "My";
                    _certs.AddRange(FindCertificates("IceSSL.FindCert", storeLocation, certStore, findCert));
                    if (_certs.Count == 0)
                    {
                        throw new InvalidConfigurationException("IceSSL: no certificates found");
                    }
                }
                else if (findCertProps.Count > 0)
                {
                    //
                    // If IceSSL.FindCert.* properties are defined, add the selected certificates
                    // to the collection.
                    //
                    foreach (KeyValuePair <string, string> entry in findCertProps)
                    {
                        string name = entry.Key;
                        string val  = entry.Value;
                        if (val.Length > 0)
                        {
                            string        storeSpec = name.Substring(findPrefix.Length);
                            StoreLocation storeLoc  = 0;
                            StoreName     storeName = 0;
                            string?       sname     = null;
                            ParseStore(name, storeSpec, ref storeLoc, ref storeName, ref sname);
                            if (sname == null)
                            {
                                sname = storeName.ToString();
                            }
                            X509Certificate2Collection coll = FindCertificates(name, storeLoc, sname, val);
                            _certs.AddRange(coll);
                        }
                    }
                    if (_certs.Count == 0)
                    {
                        throw new InvalidConfigurationException("IceSSL: no certificates found");
                    }
                }
            }

            if (_caCerts == null)
            {
                string?certAuthFile = ic.GetProperty("IceSSL.CAs");
                if (certAuthFile == null)
                {
                    certAuthFile = ic.GetProperty("IceSSL.CertAuthFile");
                }

                if (certAuthFile != null || (ic.GetPropertyAsInt("IceSSL.UsePlatformCAs") ?? 0) <= 0)
                {
                    _caCerts = new X509Certificate2Collection();
                }

                if (certAuthFile != null)
                {
                    if (!CheckPath(ref certAuthFile))
                    {
                        throw new FileNotFoundException("IceSSL: CA certificate file not found: `{certAuthFile}'",
                                                        certAuthFile);
                    }

                    try
                    {
                        using FileStream fs = File.OpenRead(certAuthFile);
                        byte[] data = new byte[fs.Length];
                        fs.Read(data, 0, data.Length);

                        string strbuf = "";
                        try
                        {
                            strbuf = System.Text.Encoding.UTF8.GetString(data);
                        }
                        catch (Exception)
                        {
                            // Ignore
                        }

                        if (strbuf.Length == data.Length)
                        {
                            int  size, startpos, endpos = 0;
                            bool first = true;
                            while (true)
                            {
                                startpos = strbuf.IndexOf("-----BEGIN CERTIFICATE-----", endpos);
                                if (startpos != -1)
                                {
                                    endpos = strbuf.IndexOf("-----END CERTIFICATE-----", startpos);
                                    size   = endpos - startpos + "-----END CERTIFICATE-----".Length;
                                }
                                else if (first)
                                {
                                    startpos = 0;
                                    endpos   = strbuf.Length;
                                    size     = strbuf.Length;
                                }
                                else
                                {
                                    break;
                                }

                                byte[] cert = new byte[size];
                                Buffer.BlockCopy(data, startpos, cert, 0, size);
                                _caCerts !.Import(cert);
                                first = false;
                            }
                        }
                        else
                        {
                            _caCerts !.Import(data);
                        }
                    }
                    catch (Exception ex)
                    {
                        throw new InvalidConfigurationException(
                                  $"IceSSL: error while attempting to load CA certificate from {certAuthFile}", ex);
                    }
                }
            }
            _initialized = true;
        }
예제 #5
0
파일: AllTests.cs 프로젝트: bailudata/ice
    public static void allTests(Test.TestHelper helper)
    {
        Ice.Communicator communicator = helper.communicator();
        Console.Out.Write("testing stringToProxy... ");
        Console.Out.Flush();
        string rf    = "test @ TestAdapter";
        var    @base = IObjectPrx.Parse(rf, communicator);

        Console.Out.WriteLine("ok");

        Console.Out.Write("testing IceGrid.Locator is present... ");
        IceGrid.ILocatorPrx locator = IceGrid.ILocatorPrx.UncheckedCast(@base);
        Console.Out.WriteLine("ok");

        Console.Out.Write("testing checked cast... ");
        Console.Out.Flush();
        ITestIntfPrx obj = ITestIntfPrx.CheckedCast(@base);

        test(obj.Equals(@base));
        Console.Out.WriteLine("ok");

        Console.Out.Write("pinging server... ");
        Console.Out.Flush();
        obj.IcePing();
        Console.Out.WriteLine("ok");

        Console.Out.Write("testing locator finder... ");
        Ice.Identity finderId = new Ice.Identity();
        finderId.category = "Ice";
        finderId.name     = "LocatorFinder";
        Ice.ILocatorFinderPrx finder = Ice.ILocatorFinderPrx.CheckedCast(communicator.getDefaultLocator().Clone(finderId));
        test(finder.GetLocator() != null);
        Console.Out.WriteLine("ok");

        Console.Out.Write("testing discovery... ");
        {
            // Add test well-known object
            IceGrid.IRegistryPrx registry = IceGrid.IRegistryPrx.Parse(
                communicator.getDefaultLocator().Identity.category + "/Registry", communicator);

            IceGrid.IAdminSessionPrx session = registry.CreateAdminSession("foo", "bar");
            session.GetAdmin().AddObjectWithType(@base, "::Test");
            session.Destroy();

            //
            // Ensure the IceGrid discovery locator can discover the
            // registries and make sure locator requests are forwarded.
            //
            var properties = communicator.GetProperties();
            properties.Remove("Ice.Default.Locator");
            properties["Ice.Plugin.IceLocatorDiscovery"]    = "IceLocatorDiscovery:IceLocatorDiscovery.PluginFactory";
            properties["IceLocatorDiscovery.Port"]          = helper.getTestPort(99).ToString();
            properties["AdapterForDiscoveryTest.AdapterId"] = "discoveryAdapter";
            properties["AdapterForDiscoveryTest.Endpoints"] = "default";

            Communicator com = new Communicator(properties);
            test(com.getDefaultLocator() != null);
            IObjectPrx.Parse("test @ TestAdapter", com).IcePing();
            IObjectPrx.Parse("test", com).IcePing();

            test(com.getDefaultLocator().GetRegistry() != null);
            test(IceGrid.ILocatorPrx.UncheckedCast(com.getDefaultLocator()).GetLocalRegistry() != null);
            test(IceGrid.ILocatorPrx.UncheckedCast(com.getDefaultLocator()).GetLocalQuery() != null);

            Ice.ObjectAdapter adapter = com.createObjectAdapter("AdapterForDiscoveryTest");
            adapter.Activate();
            adapter.Deactivate();
            com.destroy();

            //
            // Now, ensure that the IceGrid discovery locator correctly
            // handles failure to find a locator.
            //
            properties["IceLocatorDiscovery.InstanceName"] = "unknown";
            properties["IceLocatorDiscovery.RetryCount"]   = "1";
            properties["IceLocatorDiscovery.Timeout"]      = "100";
            com = new Communicator(properties);
            test(com.getDefaultLocator() != null);
            try
            {
                IObjectPrx.Parse("test @ TestAdapter", com).IcePing();
            }
            catch (Ice.NoEndpointException)
            {
            }
            try
            {
                IObjectPrx.Parse("test", com).IcePing();
            }
            catch (Ice.NoEndpointException)
            {
            }
            test(com.getDefaultLocator().GetRegistry() == null);
            test(IceGrid.ILocatorPrx.CheckedCast(com.getDefaultLocator()) == null);
            try
            {
                IceGrid.ILocatorPrx.UncheckedCast(com.getDefaultLocator()).GetLocalRegistry();
            }
            catch (Ice.OperationNotExistException)
            {
            }

            adapter = com.createObjectAdapter("AdapterForDiscoveryTest");
            adapter.Activate();
            adapter.Deactivate();

            com.destroy();

            string multicast;
            if (communicator.GetProperty("Ice.IPv6") == "1")
            {
                multicast = "\"ff15::1\"";
            }
            else
            {
                multicast = "239.255.0.1";
            }

            //
            // Test invalid lookup endpoints
            //
            properties = communicator.GetProperties();
            properties.Remove("Ice.Default.Locator");
            properties["Ice.Plugin.IceLocatorDiscovery"] = "IceLocatorDiscovery:IceLocatorDiscovery.PluginFactory";
            properties["IceLocatorDiscovery.Lookup"]     = $"udp -h {multicast} --interface unknown";
            com = new Communicator(properties);
            test(com.getDefaultLocator() != null);
            try
            {
                IObjectPrx.Parse("test @ TestAdapter", com).IcePing();
                test(false);
            }
            catch (NoEndpointException)
            {
            }
            com.destroy();

            properties = communicator.GetProperties();
            properties.Remove("Ice.Default.Locator");
            properties["IceLocatorDiscovery.RetryCount"] = "0";
            properties["Ice.Plugin.IceLocatorDiscovery"] = "IceLocatorDiscovery:IceLocatorDiscovery.PluginFactory";
            properties["IceLocatorDiscovery.Lookup"]     = $"udp -h {multicast} --interface unknown";
            com = new Communicator(properties);
            test(com.getDefaultLocator() != null);
            try
            {
                IObjectPrx.Parse("test @ TestAdapter", com).IcePing();
                test(false);
            }
            catch (NoEndpointException)
            {
            }
            com.destroy();

            properties = communicator.GetProperties();
            properties.Remove("Ice.Default.Locator");
            properties["IceLocatorDiscovery.RetryCount"] = "1";
            properties["Ice.Plugin.IceLocatorDiscovery"] = "IceLocatorDiscovery:IceLocatorDiscovery.PluginFactory";
            {
                string intf = communicator.GetProperty("IceLocatorDiscovery.Interface") ?? "";
                if (intf != "")
                {
                    intf = $" --interface \"{intf}\"";
                }
                string port = helper.getTestPort(99).ToString();
                properties["IceLocatorDiscovery.Lookup"] =
                    $"udp -h {multicast} --interface unknown:udp -h {multicast} -p {port}{intf}";
            }
            com = new Communicator(properties);
            test(com.getDefaultLocator() != null);
            try
            {
                IObjectPrx.Parse("test @ TestAdapter", com).IcePing();
            }
            catch (NoEndpointException)
            {
                test(false);
            }
            com.destroy();
        }
        Console.Out.WriteLine("ok");

        Console.Out.Write("shutting down server... ");
        Console.Out.Flush();
        obj.shutdown();
        Console.Out.WriteLine("ok");
    }
예제 #6
0
        internal MetricsMap(string mapPrefix, Ice.Communicator communicator, Dictionary <string, ISubMapFactory>?subMaps)
        {
            MetricsAdminI.ValidateProperties(mapPrefix, communicator);
            _properties = communicator.GetProperties(forPrefix: mapPrefix);

            _retain            = communicator.GetPropertyAsInt($"{mapPrefix}RetainDetached") ?? 10;
            _accept            = ParseRule(communicator, $"{mapPrefix}Accept");
            _reject            = ParseRule(communicator, $"{mapPrefix}Reject");
            _groupByAttributes = new List <string>();
            _groupBySeparators = new List <string>();

            string groupBy = communicator.GetProperty($"{mapPrefix}GroupBy") ?? "id";

            if (groupBy.Length > 0)
            {
                string v         = "";
                bool   attribute = char.IsLetter(groupBy[0]) || char.IsDigit(groupBy[0]);
                if (!attribute)
                {
                    _groupByAttributes.Add("");
                }

                foreach (char p in groupBy)
                {
                    bool isAlphaNum = char.IsLetter(p) || char.IsDigit(p) || p == '.';
                    if (attribute && !isAlphaNum)
                    {
                        _groupByAttributes.Add(v);
                        v         = "" + p;
                        attribute = false;
                    }
                    else if (!attribute && isAlphaNum)
                    {
                        _groupBySeparators.Add(v);
                        v         = "" + p;
                        attribute = true;
                    }
                    else
                    {
                        v += p;
                    }
                }

                if (attribute)
                {
                    _groupByAttributes.Add(v);
                }
                else
                {
                    _groupBySeparators.Add(v);
                }
            }

            if (subMaps != null && subMaps.Count > 0)
            {
                _subMaps = new Dictionary <string, ISubMapCloneFactory>();

                var subMapNames = new List <string>();
                foreach (KeyValuePair <string, ISubMapFactory> e in subMaps)
                {
                    subMapNames.Add(e.Key);
                    string subAllMapsPrefix = mapPrefix + "Map.";
                    string subMapPrefix     = subAllMapsPrefix + e.Key + '.';
                    if (communicator.GetProperties(forPrefix: subMapPrefix).Count == 0)
                    {
                        if (communicator.GetProperties(forPrefix: subAllMapsPrefix).Count == 0)
                        {
                            subMapPrefix = mapPrefix;
                        }
                        else
                        {
                            continue; // This sub-map isn't configured.
                        }
                    }

                    _subMaps.Add(e.Key, e.Value.CreateCloneFactory(subMapPrefix, communicator));
                }
            }
            else
            {
                _subMaps = null;
            }
        }