1 //
2 // Copyright (c) ZeroC, Inc. All rights reserved.
3 //
4 
5 namespace IceInternal
6 {
7     using System;
8     using System.Text;
9     using System.Diagnostics;
10     using System.Collections.Generic;
11     using System.Text.RegularExpressions;
12 
13     internal interface IMetricsMap
14     {
getMetrics()15         IceMX.Metrics[] getMetrics();
getFailures()16         IceMX.MetricsFailures[] getFailures();
getFailures(string id)17         IceMX.MetricsFailures getFailures(string id);
getProperties()18         Dictionary<string, string> getProperties();
19     }
20 
21     interface ISubMap
22     {
addSubMapToMetrics(IceMX.Metrics metrics)23         void addSubMapToMetrics(IceMX.Metrics metrics);
24     }
25 
26     interface ISubMapCloneFactory
27     {
create()28         ISubMap create();
29     }
30 
31     interface ISubMapFactory
32     {
createCloneFactory(string subMapPrefix, Ice.Properties properties)33         ISubMapCloneFactory createCloneFactory(string subMapPrefix, Ice.Properties properties);
34     }
35 
36     internal interface IMetricsMapFactory
37     {
38         void registerSubMap<S>(string subMap, System.Reflection.FieldInfo field) where S : IceMX.Metrics, new();
update()39         void update();
create(string mapPrefix, Ice.Properties properties)40         IMetricsMap create(string mapPrefix, Ice.Properties properties);
41     }
42 
43     internal class SubMap<S> : ISubMap where S : IceMX.Metrics, new()
44     {
SubMap(MetricsMap<S> map, System.Reflection.FieldInfo field)45         internal SubMap(MetricsMap<S> map, System.Reflection.FieldInfo field)
46         {
47             _map = map;
48             _field = field;
49         }
50 
getMatching(IceMX.MetricsHelper<S> helper)51         internal MetricsMap<S>.Entry getMatching(IceMX.MetricsHelper<S> helper)
52         {
53             return _map.getMatching(helper, null);
54         }
55 
addSubMapToMetrics(IceMX.Metrics metrics)56         public void addSubMapToMetrics(IceMX.Metrics metrics)
57         {
58             try
59             {
60                 _field.SetValue(metrics, _map.getMetrics());
61             }
62             catch(Exception)
63             {
64                 Debug.Assert(false);
65             }
66         }
67 
68         readonly private MetricsMap<S> _map;
69         readonly private System.Reflection.FieldInfo _field;
70     }
71 
72     internal class SubMapCloneFactory<S> : ISubMapCloneFactory where S : IceMX.Metrics, new()
73     {
SubMapCloneFactory(MetricsMap<S> map, System.Reflection.FieldInfo field)74         internal SubMapCloneFactory(MetricsMap<S> map, System.Reflection.FieldInfo field)
75         {
76             _map = map;
77             _field = field;
78         }
79 
create()80         public ISubMap create()
81         {
82             return new SubMap<S>(new MetricsMap<S>(_map), _field);
83         }
84 
85         readonly private MetricsMap<S> _map;
86         readonly private System.Reflection.FieldInfo _field;
87     }
88 
89     class SubMapFactory<S> : ISubMapFactory where S : IceMX.Metrics, new()
90     {
SubMapFactory(System.Reflection.FieldInfo field)91         internal SubMapFactory(System.Reflection.FieldInfo field)
92         {
93             _field = field;
94         }
95 
createCloneFactory(string subMapPrefix, Ice.Properties properties)96         public ISubMapCloneFactory createCloneFactory(string subMapPrefix, Ice.Properties properties)
97         {
98             return new SubMapCloneFactory<S>(new MetricsMap<S>(subMapPrefix, properties, null), _field);
99         }
100 
101         readonly private System.Reflection.FieldInfo _field;
102     }
103 
104     public class MetricsMap<T> : IMetricsMap where T : IceMX.Metrics, new()
105     {
106         public class Entry
107         {
Entry(MetricsMap<T> map, T obj)108             internal Entry(MetricsMap<T> map, T obj)
109             {
110                 _map = map;
111                 _object = obj;
112             }
113 
failed(string exceptionName)114             public void failed(string exceptionName)
115             {
116                 lock(_map)
117                 {
118                     ++_object.failures;
119                     int count;
120                     if(_failures == null)
121                     {
122                         _failures = new Dictionary<string, int>();
123                     }
124                     if(_failures.TryGetValue(exceptionName, out count))
125                     {
126                         _failures[exceptionName] = count + 1;
127                     }
128                     else
129                     {
130                         _failures[exceptionName] = 1;
131                     }
132                 }
133             }
134 
135             internal MetricsMap<S>.Entry getMatching<S>(string mapName, IceMX.MetricsHelper<S> helper)
136                 where S : IceMX.Metrics, new()
137             {
138                 ISubMap m;
139                 lock(_map)
140                 {
141                     if(_subMaps == null || !_subMaps.TryGetValue(mapName, out m))
142                     {
143                         m = _map.createSubMap(mapName);
144                         if(m == null)
145                         {
146                             return null;
147                         }
148                         if(_subMaps == null)
149                         {
150                             _subMaps = new Dictionary<string, ISubMap>();
151                         }
152                         _subMaps.Add(mapName, m);
153                     }
154                 }
155                 return ((SubMap<S>)m).getMatching(helper);
156             }
157 
detach(long lifetime)158             public void detach(long lifetime)
159             {
160                 lock(_map)
161                 {
162                     _object.totalLifetime += lifetime;
163                     if(--_object.current == 0)
164                     {
165                         _map.detached(this);
166                     }
167                 }
168             }
169 
execute(IceMX.Observer<T>.MetricsUpdate func)170             public void execute(IceMX.Observer<T>.MetricsUpdate func)
171             {
172                 lock(_map)
173                 {
174                     func(_object);
175                 }
176             }
177 
getMap()178             public MetricsMap<T> getMap()
179             {
180                 return _map;
181             }
182 
getFailures()183             internal IceMX.MetricsFailures getFailures()
184             {
185                 if(_failures == null)
186                 {
187                     return null;
188                 }
189                 IceMX.MetricsFailures f = new IceMX.MetricsFailures();
190                 f.id = _object.id;
191                 f.failures = new Dictionary<string, int>(_failures);
192                 return f;
193             }
194 
attach(IceMX.MetricsHelper<T> helper)195             internal void attach(IceMX.MetricsHelper<T> helper)
196             {
197                 ++_object.total;
198                 ++_object.current;
199                 helper.initMetrics(_object);
200             }
201 
isDetached()202             internal bool isDetached()
203             {
204                 return _object.current == 0;
205             }
206 
clone()207             internal IceMX.Metrics clone()
208             {
209                 T metrics = (T)_object.Clone();
210                 if(_subMaps != null)
211                 {
212                     foreach(ISubMap s in _subMaps.Values)
213                     {
214                         s.addSubMapToMetrics(metrics);
215                     }
216                 }
217                 return metrics;
218             }
219 
getId()220             internal string getId()
221             {
222                 return _object.id;
223             }
224 
225             private MetricsMap<T> _map;
226             private T _object;
227             private Dictionary<string, int> _failures;
228             private Dictionary<string, ISubMap> _subMaps;
229         }
230 
MetricsMap(string mapPrefix, Ice.Properties props, Dictionary<string, ISubMapFactory> subMaps)231         internal MetricsMap(string mapPrefix, Ice.Properties props, Dictionary<string, ISubMapFactory> subMaps)
232         {
233             MetricsAdminI.validateProperties(mapPrefix, props);
234             _properties = props.getPropertiesForPrefix(mapPrefix);
235 
236             _retain = props.getPropertyAsIntWithDefault(mapPrefix + "RetainDetached", 10);
237             _accept = parseRule(props, mapPrefix + "Accept");
238             _reject = parseRule(props, mapPrefix + "Reject");
239             _groupByAttributes = new List<string>();
240             _groupBySeparators = new List<string>();
241 
242             string groupBy = props.getPropertyWithDefault(mapPrefix + "GroupBy", "id");
243             if(groupBy.Length > 0)
244             {
245                 string v = "";
246                 bool attribute = char.IsLetter(groupBy[0]) || char.IsDigit(groupBy[0]);
247                 if(!attribute)
248                 {
249                     _groupByAttributes.Add("");
250                 }
251 
252                 foreach(char p in groupBy)
253                 {
254                     bool isAlphaNum = char.IsLetter(p) || char.IsDigit(p) || p == '.';
255                     if(attribute && !isAlphaNum)
256                     {
257                         _groupByAttributes.Add(v);
258                         v = "" + p;
259                         attribute = false;
260                     }
261                     else if(!attribute && isAlphaNum)
262                     {
263                         _groupBySeparators.Add(v);
264                         v = "" + p;
265                         attribute = true;
266                     }
267                     else
268                     {
269                         v += p;
270                     }
271                 }
272 
273                 if(attribute)
274                 {
275                     _groupByAttributes.Add(v);
276                 }
277                 else
278                 {
279                     _groupBySeparators.Add(v);
280                 }
281             }
282 
283             if(subMaps != null && subMaps.Count > 0)
284             {
285                 _subMaps = new Dictionary<string, ISubMapCloneFactory>();
286 
287                 List<string> subMapNames = new List<string>();
288                 foreach(KeyValuePair<string, ISubMapFactory> e in subMaps)
289                 {
290                     subMapNames.Add(e.Key);
291                     string subMapsPrefix = mapPrefix + "Map.";
292                     string subMapPrefix = subMapsPrefix + e.Key + '.';
293                     if(props.getPropertiesForPrefix(subMapPrefix).Count == 0)
294                     {
295                         if(props.getPropertiesForPrefix(subMapsPrefix).Count == 0)
296                         {
297                             subMapPrefix = mapPrefix;
298                         }
299                         else
300                         {
301                             continue; // This sub-map isn't configured.
302                         }
303                     }
304 
305                     _subMaps.Add(e.Key, e.Value.createCloneFactory(subMapPrefix, props));
306                 }
307             }
308             else
309             {
310                 _subMaps = null;
311             }
312         }
313 
MetricsMap(MetricsMap<T> map)314         internal MetricsMap(MetricsMap<T> map)
315         {
316             _properties = map._properties;
317             _groupByAttributes = map._groupByAttributes;
318             _groupBySeparators = map._groupBySeparators;
319             _retain = map._retain;
320             _accept = map._accept;
321             _reject = map._reject;
322             _subMaps = map._subMaps;
323         }
324 
getProperties()325         public Dictionary<string, string> getProperties()
326         {
327             return _properties;
328         }
329 
getMetrics()330         public IceMX.Metrics[] getMetrics()
331         {
332             lock(this)
333             {
334                 IceMX.Metrics[] metrics = new IceMX.Metrics[_objects.Count];
335                 int i = 0;
336                 foreach(Entry e in _objects.Values)
337                 {
338                     metrics[i++] = e.clone();
339                 }
340                 return metrics;
341             }
342         }
343 
getFailures()344         public IceMX.MetricsFailures[] getFailures()
345         {
346             lock(this)
347             {
348                 List<IceMX.MetricsFailures> failures = new List<IceMX.MetricsFailures>();
349                 foreach(Entry e in _objects.Values)
350                 {
351                     IceMX.MetricsFailures f = e.getFailures();
352                     if(f != null)
353                     {
354                         failures.Add(f);
355                     }
356                 }
357                 return failures.ToArray();
358             }
359         }
360 
getFailures(string id)361         public IceMX.MetricsFailures getFailures(string id)
362         {
363             lock(this)
364             {
365                 Entry e;
366                 if(_objects.TryGetValue(id, out e))
367                 {
368                     return e.getFailures();
369                 }
370                 return null;
371             }
372         }
373 
createSubMap(string subMapName)374         ISubMap createSubMap(string subMapName)
375         {
376             if(_subMaps == null)
377             {
378                 return null;
379             }
380             ISubMapCloneFactory factory;
381             if(_subMaps.TryGetValue(subMapName, out factory))
382             {
383                 return factory.create();
384             }
385             return null;
386         }
387 
getMatching(IceMX.MetricsHelper<T> helper, Entry previous)388         public Entry getMatching(IceMX.MetricsHelper<T> helper, Entry previous)
389         {
390             //
391             // Check the accept and reject filters.
392             //
393             foreach(KeyValuePair<string, Regex> e in _accept)
394             {
395                 if(!match(e.Key, e.Value, helper, false))
396                 {
397                     return null;
398                 }
399             }
400 
401             foreach(KeyValuePair<string, Regex> e in _reject)
402             {
403                 if(match(e.Key, e.Value, helper, true))
404                 {
405                     return null;
406                 }
407             }
408 
409             //
410             // Compute the key from the GroupBy property.
411             //
412             string key;
413             try
414             {
415                 if(_groupByAttributes.Count == 1)
416                 {
417                     key = helper.resolve(_groupByAttributes[0]);
418                 }
419                 else
420                 {
421                     StringBuilder os = new StringBuilder();
422                     IEnumerator<string> q = _groupBySeparators.GetEnumerator();
423                     foreach(string p in _groupByAttributes)
424                     {
425                         os.Append(helper.resolve(p));
426                         if(q.MoveNext())
427                         {
428                             os.Append(q.Current);
429                         }
430                     }
431                     key = os.ToString();
432                 }
433             }
434             catch(Exception)
435             {
436                 return null;
437             }
438 
439             //
440             // Lookup the metrics object.
441             //
442             lock(this)
443             {
444                 if(previous != null && previous.getId().Equals(key))
445                 {
446                     Debug.Assert(_objects[key] == previous);
447                     return previous;
448                 }
449 
450                 Entry e;
451                 if(!_objects.TryGetValue(key, out e))
452                 {
453                     try
454                     {
455                         T t = new T();
456                         t.id = key;
457                         e = new Entry(this, t);
458                         _objects.Add(key, e);
459                     }
460                     catch(Exception)
461                     {
462                         Debug.Assert(false);
463                     }
464                 }
465                 e.attach(helper);
466                 return e;
467             }
468         }
469 
detached(Entry entry)470         private void detached(Entry entry)
471         {
472             if(_retain == 0)
473             {
474                 return;
475             }
476 
477             if(_detachedQueue == null)
478             {
479                 _detachedQueue = new LinkedList<Entry>();
480             }
481             Debug.Assert(_detachedQueue.Count <= _retain);
482 
483             // Compress the queue by removing entries which are no longer detached.
484             LinkedListNode<Entry> p = _detachedQueue.First;
485             while(p != null)
486             {
487                 LinkedListNode<Entry> next = p.Next;
488                 if(p.Value == entry || !p.Value.isDetached())
489                 {
490                     _detachedQueue.Remove(p);
491                 }
492                 p = next;
493             }
494 
495             // If there's still no room, remove the oldest entry (at the front).
496             if(_detachedQueue.Count == _retain)
497             {
498                 _objects.Remove(_detachedQueue.First.Value.getId());
499                 _detachedQueue.RemoveFirst();
500             }
501 
502             // Add the entry at the back of the queue.
503             _detachedQueue.AddLast(entry);
504         }
505 
parseRule(Ice.Properties properties, string name)506         private Dictionary<string, Regex> parseRule(Ice.Properties properties, string name)
507         {
508             Dictionary<string, Regex> pats = new Dictionary<string, Regex>();
509             Dictionary<string, string> rules = properties.getPropertiesForPrefix(name + '.');
510             foreach(KeyValuePair<string, string> e in rules)
511             {
512                 pats.Add(e.Key.Substring(name.Length + 1), new Regex(e.Value));
513             }
514             return pats;
515         }
516 
match(string attribute, Regex regex, IceMX.MetricsHelper<T> helper, bool reject)517         private bool match(string attribute, Regex regex, IceMX.MetricsHelper<T> helper, bool reject)
518         {
519             string value;
520             try
521             {
522                 value = helper.resolve(attribute);
523             }
524             catch(Exception)
525             {
526                 return !reject;
527             }
528             return regex.IsMatch(value);
529         }
530 
531         readonly private Dictionary<string, string> _properties;
532         readonly private List<string> _groupByAttributes;
533         readonly private List<string> _groupBySeparators;
534         readonly private int _retain;
535         readonly private Dictionary<string, Regex> _accept;
536         readonly private Dictionary<string, Regex> _reject;
537 
538         readonly private Dictionary<string, Entry> _objects = new Dictionary<string, Entry>();
539         readonly private Dictionary<string, ISubMapCloneFactory> _subMaps;
540         private LinkedList<Entry> _detachedQueue;
541     }
542 
543     internal class MetricsViewI
544     {
MetricsViewI(string name)545         internal MetricsViewI(string name)
546         {
547             _name = name;
548         }
549 
addOrUpdateMap(Ice.Properties properties, string mapName, IMetricsMapFactory factory, Ice.Logger logger)550         internal bool addOrUpdateMap(Ice.Properties properties, string mapName, IMetricsMapFactory factory,
551                                    Ice.Logger logger)
552         {
553             //
554             // Add maps to views configured with the given map.
555             //
556             string viewPrefix = "IceMX.Metrics." + _name + ".";
557             string mapsPrefix = viewPrefix + "Map.";
558             Dictionary<string, string> mapsProps = properties.getPropertiesForPrefix(mapsPrefix);
559 
560             string mapPrefix;
561             Dictionary<string, string> mapProps = new Dictionary<string, string>();
562             if(mapsProps.Count > 0)
563             {
564                 mapPrefix = mapsPrefix + mapName + ".";
565                 mapProps = properties.getPropertiesForPrefix(mapPrefix);
566                 if(mapProps.Count == 0)
567                 {
568                     // This map isn't configured for this view.
569                     return _maps.Remove(mapName);
570                 }
571             }
572             else
573             {
574                 mapPrefix = viewPrefix;
575                 mapProps = properties.getPropertiesForPrefix(mapPrefix);
576             }
577 
578             if(properties.getPropertyAsInt(mapPrefix + "Disabled") > 0)
579             {
580                 // This map is disabled for this view.
581                 return _maps.Remove(mapName);
582             }
583 
584             IMetricsMap m;
585             if(_maps.TryGetValue(mapName, out m) &&
586                IceUtilInternal.Collections.DictionaryEquals(m.getProperties(), mapProps))
587             {
588                 return false; // The map configuration didn't change, no need to re-create.
589             }
590 
591             try
592             {
593                 _maps[mapName] = factory.create(mapPrefix, properties);
594             }
595             catch(Exception ex)
596             {
597                 logger.warning("unexpected exception while creating metrics map:\n" + ex);
598                 _maps.Remove(mapName);
599             }
600             return true;
601         }
602 
removeMap(string mapName)603         internal bool removeMap(string mapName)
604         {
605             return _maps.Remove(mapName);
606         }
607 
getMetrics()608         internal Dictionary<string, IceMX.Metrics[]> getMetrics()
609         {
610             Dictionary<string, IceMX.Metrics[]> metrics = new Dictionary<string, IceMX.Metrics[]>();
611             foreach(KeyValuePair<string, IMetricsMap> e in _maps)
612             {
613                 IceMX.Metrics[] m = e.Value.getMetrics();
614                 if(m != null)
615                 {
616                     metrics.Add(e.Key, m);
617                 }
618             }
619             return metrics;
620         }
621 
getFailures(string mapName)622         internal IceMX.MetricsFailures[] getFailures(string mapName)
623         {
624             IMetricsMap m;
625             if(_maps.TryGetValue(mapName, out m))
626             {
627                 return m.getFailures();
628             }
629             return null;
630         }
631 
getFailures(string mapName, string id)632         internal IceMX.MetricsFailures getFailures(string mapName, string id)
633         {
634             IMetricsMap m;
635             if(_maps.TryGetValue(mapName, out m))
636             {
637                 return m.getFailures(id);
638             }
639             return null;
640         }
641 
getMaps()642         internal ICollection<string> getMaps()
643         {
644             return _maps.Keys;
645         }
646 
647         internal MetricsMap<T> getMap<T>(string mapName) where T : IceMX.Metrics, new()
648         {
649             IMetricsMap m;
650             if(_maps.TryGetValue(mapName, out m))
651             {
652                 return (MetricsMap<T>)m;
653             }
654             return null;
655         }
656 
657         readonly private string _name;
658         readonly private Dictionary<string, IMetricsMap> _maps = new Dictionary<string, IMetricsMap>();
659     }
660 
661     public class MetricsAdminI : IceMX.MetricsAdminDisp_, Ice.PropertiesAdminUpdateCallback
662     {
663         readonly static private string[] suffixes =
664             {
665                 "Disabled",
666                 "GroupBy",
667                 "Accept.*",
668                 "Reject.*",
669                 "RetainDetached",
670                 "Map.*",
671             };
672 
validateProperties(string prefix, Ice.Properties properties)673         public static void validateProperties(string prefix, Ice.Properties properties)
674         {
675             Dictionary<string, string> props = properties.getPropertiesForPrefix(prefix);
676             List<string> unknownProps = new List<string>();
677             foreach(string prop in props.Keys)
678             {
679                 bool valid = false;
680                 foreach(string suffix in suffixes)
681                 {
682                     if(IceUtilInternal.StringUtil.match(prop, prefix + suffix, false))
683                     {
684                         valid = true;
685                         break;
686                     }
687                 }
688 
689                 if(!valid)
690                 {
691                     unknownProps.Add(prop);
692                 }
693             }
694 
695             if(unknownProps.Count != 0 && properties.getPropertyAsIntWithDefault("Ice.Warn.UnknownProperties", 1) > 0)
696             {
697                 StringBuilder message = new StringBuilder("found unknown IceMX properties for `");
698                 message.Append(prefix.Substring(0, prefix.Length - 1));
699                 message.Append("':");
700                 foreach(string p in unknownProps)
701                 {
702                     message.Append("\n    ");
703                     message.Append(p);
704                 }
705                 Ice.Util.getProcessLogger().warning(message.ToString());
706             }
707         }
708 
709         class MetricsMapFactory<T> : IMetricsMapFactory where T : IceMX.Metrics, new()
710         {
MetricsMapFactory(Action updater)711             public MetricsMapFactory(Action updater)
712             {
713                 _updater = updater;
714             }
715 
update()716             public void update()
717             {
718                 Debug.Assert(_updater != null);
719                 _updater();
720             }
721 
create(string mapPrefix, Ice.Properties properties)722             public IMetricsMap create(string mapPrefix, Ice.Properties properties)
723             {
724                 return new MetricsMap<T>(mapPrefix, properties, _subMaps);
725             }
726 
727             public void registerSubMap<S>(string subMap, System.Reflection.FieldInfo field)
728                 where S : IceMX.Metrics, new()
729             {
730                 _subMaps.Add(subMap, new SubMapFactory<S>(field));
731             }
732 
733             readonly private Action _updater;
734             readonly private Dictionary<string, ISubMapFactory> _subMaps = new Dictionary<string, ISubMapFactory>();
735         }
736 
MetricsAdminI(Ice.Properties properties, Ice.Logger logger)737         public MetricsAdminI(Ice.Properties properties, Ice.Logger logger)
738         {
739             _logger = logger;
740             _properties = properties;
741             updateViews();
742         }
743 
updateViews()744         public void updateViews()
745         {
746             HashSet<IMetricsMapFactory> updatedMaps = new HashSet<IMetricsMapFactory>();
747             lock(this)
748             {
749                 string viewsPrefix = "IceMX.Metrics.";
750                 Dictionary<string, string> viewsProps = _properties.getPropertiesForPrefix(viewsPrefix);
751                 Dictionary<string, MetricsViewI> views = new Dictionary<string, MetricsViewI>();
752                 _disabledViews.Clear();
753                 foreach(KeyValuePair<string, string> e in viewsProps)
754                 {
755                     string viewName = e.Key.Substring(viewsPrefix.Length);
756                     int dotPos = viewName.IndexOf('.');
757                     if(dotPos > 0)
758                     {
759                         viewName = viewName.Substring(0, dotPos);
760                     }
761 
762                     if(views.ContainsKey(viewName) || _disabledViews.Contains(viewName))
763                     {
764                         continue; // View already configured.
765                     }
766 
767                     validateProperties(viewsPrefix + viewName + ".", _properties);
768 
769                     if(_properties.getPropertyAsIntWithDefault(viewsPrefix + viewName + ".Disabled", 0) > 0)
770                     {
771                         _disabledViews.Add(viewName);
772                         continue; // The view is disabled
773                     }
774 
775                     //
776                     // Create the view or update it.
777                     //
778                     MetricsViewI v;
779                     if(!_views.TryGetValue(viewName, out v))
780                     {
781                         v = new MetricsViewI(viewName);
782                     }
783                     views[viewName] = v;
784 
785                     foreach(KeyValuePair<string, IMetricsMapFactory> f in _factories)
786                     {
787                         if(v.addOrUpdateMap(_properties, f.Key, f.Value, _logger))
788                         {
789                             updatedMaps.Add(f.Value);
790                         }
791                     }
792                 }
793 
794                 Dictionary<string, MetricsViewI> tmp = _views;
795                 _views = views;
796                 views = tmp;
797 
798                 //
799                 // Go through removed views to collect maps to update.
800                 //
801                 foreach(KeyValuePair<string, MetricsViewI> v in views)
802                 {
803                     if(!_views.ContainsKey(v.Key))
804                     {
805                         foreach(string n in v.Value.getMaps())
806                         {
807                             updatedMaps.Add(_factories[n]);
808                         }
809                     }
810                 }
811             }
812 
813             //
814             // Call the updaters to update the maps.
815             //
816             foreach(IMetricsMapFactory f in updatedMaps)
817             {
818                 f.update();
819             }
820         }
821 
getMetricsViewNames(out string[] disabledViews, Ice.Current current)822         override public string[] getMetricsViewNames(out string[] disabledViews, Ice.Current current)
823         {
824             lock(this)
825             {
826                 disabledViews = _disabledViews.ToArray();
827                 return new List<String>(_views.Keys).ToArray();
828             }
829         }
830 
enableMetricsView(string name, Ice.Current current)831         override public void enableMetricsView(string name, Ice.Current current)
832         {
833             lock(this)
834             {
835                 getMetricsView(name); // Throws if unknown view.
836                 _properties.setProperty("IceMX.Metrics." + name + ".Disabled", "0");
837             }
838             updateViews();
839         }
840 
disableMetricsView(string name, Ice.Current current)841         override public void disableMetricsView(string name, Ice.Current current)
842         {
843             lock(this)
844             {
845                 getMetricsView(name); // Throws if unknown view.
846                 _properties.setProperty("IceMX.Metrics." + name + ".Disabled", "1");
847             }
848             updateViews();
849         }
850 
getMetricsView(string viewName, out long timestamp, Ice.Current current)851         override public Dictionary<string, IceMX.Metrics[]> getMetricsView(string viewName, out long timestamp,
852                                                                            Ice.Current current)
853         {
854             lock(this)
855             {
856                 MetricsViewI view = getMetricsView(viewName);
857                 timestamp = Time.currentMonotonicTimeMillis();
858                 if(view != null)
859                 {
860                     return view.getMetrics();
861                 }
862                 return new Dictionary<string, IceMX.Metrics[]>();
863             }
864         }
865 
getMapMetricsFailures(string viewName, string mapName, Ice.Current c)866         override public IceMX.MetricsFailures[] getMapMetricsFailures(string viewName, string mapName, Ice.Current c)
867         {
868             lock(this)
869             {
870                 MetricsViewI view = getMetricsView(viewName);
871                 if(view != null)
872                 {
873                     return view.getFailures(mapName);
874                 }
875                 return new IceMX.MetricsFailures[0];
876             }
877         }
878 
getMetricsFailures(string viewName, string mapName, string id, Ice.Current c)879         override public IceMX.MetricsFailures getMetricsFailures(string viewName, string mapName, string id,
880                                                                  Ice.Current c)
881         {
882             lock(this)
883             {
884                 MetricsViewI view = getMetricsView(viewName);
885                 if(view != null)
886                 {
887                     return view.getFailures(mapName, id);
888                 }
889                 return new IceMX.MetricsFailures();
890             }
891         }
892 
893         public void registerMap<T>(string map, Action updater)
894             where T : IceMX.Metrics, new()
895         {
896             bool updated;
897             MetricsMapFactory<T> factory;
898             lock(this)
899             {
900                 factory = new MetricsMapFactory<T>(updater);
901                 _factories.Add(map, factory);
902                 updated = addOrUpdateMap(map, factory);
903             }
904             if(updated)
905             {
906                 factory.update();
907             }
908         }
909 
910         public void registerSubMap<S>(string map, string subMap, System.Reflection.FieldInfo field)
911             where S : IceMX.Metrics, new()
912         {
913             bool updated;
914             IMetricsMapFactory factory;
915             lock(this)
916             {
917                 if(!_factories.TryGetValue(map, out factory))
918                 {
919                     return;
920                 }
921                 factory.registerSubMap<S>(subMap, field);
922                 removeMap(map);
923                 updated = addOrUpdateMap(map, factory);
924             }
925             if(updated)
926             {
927                 factory.update();
928             }
929         }
930 
unregisterMap(string mapName)931         public void unregisterMap(string mapName)
932         {
933             bool updated;
934             IMetricsMapFactory factory;
935             lock(this)
936             {
937                 if(!_factories.TryGetValue(mapName, out factory))
938                 {
939                     return;
940                 }
941                 _factories.Remove(mapName);
942                 updated = removeMap(mapName);
943             }
944             if(updated)
945             {
946                 factory.update();
947             }
948         }
949 
950         public List<MetricsMap<T>> getMaps<T>(string mapName) where T : IceMX.Metrics, new()
951         {
952             List<MetricsMap<T>> maps = new List<MetricsMap<T>>();
953             foreach(MetricsViewI v in _views.Values)
954             {
955                 MetricsMap<T> map = v.getMap<T>(mapName);
956                 if(map != null)
957                 {
958                     maps.Add(map);
959                 }
960             }
961             return maps;
962         }
963 
getLogger()964         public Ice.Logger getLogger()
965         {
966             return _logger;
967         }
968 
updated(Dictionary<string, string> props)969         public void updated(Dictionary<string, string> props)
970         {
971             foreach(KeyValuePair<string, string> e in props)
972             {
973                 if(e.Key.IndexOf("IceMX.") == 0)
974                 {
975                     // Udpate the metrics views using the new configuration.
976                     try
977                     {
978                         updateViews();
979                     }
980                     catch(Exception ex)
981                     {
982                         _logger.warning("unexpected exception while updating metrics view configuration:\n" +
983                                         ex.ToString());
984                     }
985                     return;
986                 }
987             }
988         }
989 
getMetricsView(string name)990         private MetricsViewI getMetricsView(string name)
991         {
992             MetricsViewI view;
993             if(!_views.TryGetValue(name, out view))
994             {
995                 if(!_disabledViews.Contains(name))
996                 {
997                     throw new IceMX.UnknownMetricsView();
998                 }
999                 return null;
1000             }
1001             return view;
1002         }
1003 
addOrUpdateMap(string mapName, IMetricsMapFactory factory)1004         private bool addOrUpdateMap(string mapName, IMetricsMapFactory factory)
1005         {
1006             bool updated = false;
1007             foreach(MetricsViewI v in _views.Values)
1008             {
1009                 updated |= v.addOrUpdateMap(_properties, mapName, factory, _logger);
1010             }
1011             return updated;
1012         }
1013 
removeMap(string mapName)1014         private bool removeMap(string mapName)
1015         {
1016             bool updated = false;
1017             foreach(MetricsViewI v in _views.Values)
1018             {
1019                 updated |= v.removeMap(mapName);
1020             }
1021             return updated;
1022         }
1023 
1024         private Ice.Properties _properties;
1025         readonly private Ice.Logger _logger;
1026         readonly private Dictionary<string, IMetricsMapFactory> _factories =
1027             new Dictionary<string, IMetricsMapFactory>();
1028         private Dictionary<string, MetricsViewI> _views = new Dictionary<string, MetricsViewI>();
1029         private List<string> _disabledViews = new List<string>();
1030     }
1031 }
1032