< Summary

Information
Class: Ice.NativePropertiesAdmin
Assembly: Ice
File(s): /_/csharp/src/Ice/NativePropertiesAdmin.cs
Tag: 125_37167941578
Line coverage
62%
Covered lines: 50
Uncovered lines: 30
Coverable lines: 80
Total lines: 214
Line coverage: 62.5%
Branch coverage
55%
Covered branches: 29
Total branches: 52
Branch coverage: 55.7%
Method coverage
83%
Covered methods: 5
Fully covered methods: 3
Total methods: 6
Method coverage: 83.3%
Full method coverage: 50%

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
getProperty(...)100%11100%
getPropertiesForPrefix(...)100%11100%
setProperties(...)55.77%2335259.38%
addUpdateCallback(...)100%11100%
removeUpdateCallback(...)100%210%

File(s)

/_/csharp/src/Ice/NativePropertiesAdmin.cs

#LineLine coverage
 1// Copyright (c) ZeroC, Inc.
 2
 3#nullable enable
 4
 5using System.Diagnostics;
 6
 7namespace Ice;
 8
 9/// <summary>
 10/// The default implementation of the "Properties" admin facet.
 11/// </summary>
 12public sealed class NativePropertiesAdmin : PropertiesAdminDisp_
 13{
 14    private readonly Properties _properties;
 15    private readonly Logger _logger;
 116    private readonly List<Action<Dictionary<string, string>>> _updateCallbacks = new();
 117    private readonly object _mutex = new();
 18    private const string _traceCategory = "Admin.Properties";
 19
 120    public override string getProperty(string key, Current current) => _properties.getProperty(key);
 21
 22    public override Dictionary<string, string> getPropertiesForPrefix(string prefix, Current current) =>
 123        _properties.getPropertiesForPrefix(prefix);
 24
 25    public override void setProperties(Dictionary<string, string> newProperties, Current current)
 26    {
 27        // The callbacks and the change set to pass to them, captured under the lock and invoked after releasing it.
 28        // This avoids a lock-order inversion: a callback may acquire an application lock, while another thread may
 29        // call into the admin (e.g. add/removeUpdateCallback) while holding that same lock.
 130        List<Action<Dictionary<string, string>>>? callbacks = null;
 131        Dictionary<string, string>? changes = null;
 32
 133        lock (_mutex)
 34        {
 135            Dictionary<string, string> old = _properties.getPropertiesForPrefix("");
 136            int traceLevel = _properties.getIcePropertyAsInt("Ice.Trace.Admin.Properties");
 37
 38            // Compute the difference between the new property set and the existing property set:
 39            //
 40            // 1) Any properties in the new set that were not defined in the existing set.
 41            //
 42            // 2) Any properties that appear in both sets but with different values.
 43            //
 44            // 3) Any properties not present in the new set but present in the existing set.
 45            //    In other words, the property has been removed.
 46
 147            var added = new Dictionary<string, string>();
 148            var changed = new Dictionary<string, string>();
 149            var removed = new Dictionary<string, string>();
 150            foreach (KeyValuePair<string, string> e in newProperties)
 51            {
 152                string key = e.Key;
 153                string value = e.Value;
 154                if (!old.ContainsKey(key))
 55                {
 156                    if (value.Length > 0)
 57                    {
 58                        //
 59                        // This property is new.
 60                        //
 161                        added.Add(key, value);
 62                    }
 63                }
 64                else
 65                {
 166                    if (!old.TryGetValue(key, out string? v) || !value.Equals(v, StringComparison.Ordinal))
 67                    {
 168                        if (value.Length == 0)
 69                        {
 70                            //
 71                            // This property was removed.
 72                            //
 173                            removed.Add(key, value);
 74                        }
 75                        else
 76                        {
 77                            //
 78                            // This property has changed.
 79                            //
 180                            changed.Add(key, value);
 81                        }
 82                    }
 83
 184                    old.Remove(key);
 85                }
 86            }
 87
 188            if (traceLevel > 0 && (added.Count > 0 || changed.Count > 0 || removed.Count > 0))
 89            {
 090                var message = new System.Text.StringBuilder("Summary of property changes");
 91
 092                if (added.Count > 0)
 93                {
 094                    message.Append("\nNew properties:");
 095                    foreach (KeyValuePair<string, string> e in added)
 96                    {
 097                        message.Append("\n  ");
 098                        message.Append(e.Key);
 099                        if (traceLevel > 1)
 100                        {
 0101                            message.Append(" = ");
 0102                            message.Append(e.Value);
 103                        }
 104                    }
 105                }
 106
 0107                if (changed.Count > 0)
 108                {
 0109                    message.Append("\nChanged properties:");
 0110                    foreach (KeyValuePair<string, string> e in changed)
 111                    {
 0112                        message.Append("\n  ");
 0113                        message.Append(e.Key);
 0114                        if (traceLevel > 1)
 115                        {
 0116                            message.Append(" = ");
 0117                            message.Append(e.Value);
 0118                            message.Append(" (old value = ");
 0119                            message.Append(_properties.getProperty(e.Key));
 0120                            message.Append(')');
 121                        }
 122                    }
 123                }
 124
 0125                if (removed.Count > 0)
 126                {
 0127                    message.Append("\nRemoved properties:");
 0128                    foreach (KeyValuePair<string, string> e in removed)
 129                    {
 0130                        message.Append("\n  ");
 0131                        message.Append(e.Key);
 132                    }
 133                }
 134
 0135                _logger.trace(_traceCategory, message.ToString());
 136            }
 137
 138            //
 139            // Update the property set.
 140            //
 141
 1142            foreach (KeyValuePair<string, string> e in added)
 143            {
 1144                _properties.setProperty(e.Key, e.Value);
 145            }
 146
 1147            foreach (KeyValuePair<string, string> e in changed)
 148            {
 1149                _properties.setProperty(e.Key, e.Value);
 150            }
 151
 1152            foreach (KeyValuePair<string, string> e in removed)
 153            {
 1154                _properties.setProperty(e.Key, "");
 155            }
 156
 1157            if (_updateCallbacks.Count > 0)
 158            {
 1159                changes = new Dictionary<string, string>(added);
 1160                foreach (KeyValuePair<string, string> e in changed)
 161                {
 1162                    changes.Add(e.Key, e.Value);
 163                }
 1164                foreach (KeyValuePair<string, string> e in removed)
 165                {
 1166                    changes.Add(e.Key, e.Value);
 167                }
 168
 169                // Copy the callbacks to allow callbacks to update the callbacks.
 1170                callbacks = new List<Action<Dictionary<string, string>>>(_updateCallbacks);
 171            }
 1172        }
 173
 1174        if (callbacks is not null)
 175        {
 176            Debug.Assert(changes is not null);
 1177            foreach (Action<Dictionary<string, string>> callback in callbacks)
 178            {
 179                // The callback should not throw any exception.
 1180                callback(changes);
 181            }
 182        }
 1183    }
 184
 185    /// <summary>
 186    /// Registers an update callback that will be invoked when a property update occurs.
 187    /// </summary>
 188    /// <param name="cb">The callback.</param>
 189    public void addUpdateCallback(System.Action<Dictionary<string, string>> cb)
 190    {
 1191        lock (_mutex)
 192        {
 1193            _updateCallbacks.Add(cb);
 1194        }
 1195    }
 196
 197    /// <summary>
 198    /// Unregisters an update callback previously registered with <see cref="addUpdateCallback" /> .
 199    /// </summary>
 200    /// <param name="cb">The callback.</param>
 201    public void removeUpdateCallback(System.Action<Dictionary<string, string>> cb)
 202    {
 0203        lock (_mutex)
 204        {
 0205            _updateCallbacks.Remove(cb);
 0206        }
 0207    }
 208
 1209    internal NativePropertiesAdmin(Internal.Instance instance)
 210    {
 1211        _properties = instance.initializationData().properties!;
 1212        _logger = instance.initializationData().logger!;
 1213    }
 214}