diff options
Diffstat (limited to 'java/src/IceInternal/MetricsMap.java')
-rw-r--r-- | java/src/IceInternal/MetricsMap.java | 530 |
1 files changed, 530 insertions, 0 deletions
diff --git a/java/src/IceInternal/MetricsMap.java b/java/src/IceInternal/MetricsMap.java new file mode 100644 index 00000000000..d1efd1d4bb7 --- /dev/null +++ b/java/src/IceInternal/MetricsMap.java @@ -0,0 +1,530 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2012 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +package IceInternal; + +public class MetricsMap<T extends IceMX.Metrics> +{ + final private String[] mapSuffixes = + { + "GroupBy", + "Accept.*", + "Reject.*", + "RetainDetached", + }; + + public class Entry implements Comparable<Entry> + { + Entry(T obj) + { + _object = obj; + } + + synchronized public void + failed(String exceptionName) + { + ++_object.failures; + Integer count = _failures.get(exceptionName); + _failures.put(exceptionName, new Integer(count == null ? 1 : count + 1)); + } + + synchronized IceMX.MetricsFailures + getFailures() + { + if(_failures.isEmpty()) + { + return null; + } + IceMX.MetricsFailures f = new IceMX.MetricsFailures(); + f.id = _object.id; + f.failures = new java.util.HashMap<String, Integer>(_failures); + return f; + } + + @SuppressWarnings("unchecked") + public <S extends IceMX.Metrics> MetricsMap<S>.Entry + getMatching(String mapName, IceMX.MetricsHelper<S> helper, Class<S> cl) + { + SubMap<S> m; + synchronized(this) + { + m = _subMaps != null ? (SubMap<S>)_subMaps.get(mapName) : null; + if(m == null) + { + m = createSubMap(mapName, cl); + if(m == null) + { + return null; + } + if(_subMaps == null) + { + _subMaps = new java.util.HashMap<String, SubMap<?>>(); + } + _subMaps.put(mapName, m); + } + } + return m.getMatching(helper); + } + + synchronized public void + attach(IceMX.MetricsHelper<T> helper) + { + ++_object.total; + ++_object.current; + helper.initMetrics(_object); + } + + public void + detach(long lifetime) + { + synchronized(this) + { + _object.totalLifetime += lifetime; + if(--_object.current > 0) + { + return; + } + } + detached(this); + } + + synchronized public boolean + isDetached() + { + return _object.current == 0; + } + + synchronized public void + execute(IceMX.Observer.MetricsUpdate<T> func) + { + func.update(_object); + } + + @SuppressWarnings("unchecked") + synchronized public IceMX.Metrics + clone() + { + T metrics = (T)_object.clone(); + if(_subMaps != null) + { + for(SubMap s : _subMaps.values()) + { + s.addSubMapToMetrics(metrics); + } + } + return metrics; + } + + public int + compareTo(Entry e) + { + return _object.id.compareTo(e._object.id); + } + + private T _object; + private java.util.Map<String, Integer> _failures = new java.util.HashMap<String, Integer>(); + private java.util.Map<String, SubMap<?>> _subMaps; + }; + + static class SubMap<S extends IceMX.Metrics> + { + public + SubMap(MetricsMap<S> map, java.lang.reflect.Field field) + { + _map = map; + _field = field; + } + + public MetricsMap<S>.Entry + getMatching(IceMX.MetricsHelper<S> helper) + { + return _map.getMatching(helper); + } + + public void + addSubMapToMetrics(IceMX.Metrics metrics) + { + try + { + _field.set(metrics, _map.getMetrics()); + } + catch(Exception ex) + { + assert(false); + } + } + + final private MetricsMap<S> _map; + final private java.lang.reflect.Field _field; + }; + + static class SubMapCloneFactory<S extends IceMX.Metrics> + { + public SubMapCloneFactory(MetricsMap<S> map, java.lang.reflect.Field field) + { + _map = map; + _field = field; + } + + public SubMap<S> + create() + { + return new SubMap<S>(new MetricsMap<S>(_map), _field); + } + + final private MetricsMap<S> _map; + final private java.lang.reflect.Field _field; + }; + + static class SubMapFactory<S extends IceMX.Metrics> + { + SubMapFactory(Class<S> cl, java.lang.reflect.Field field) + { + _class = cl; + _field = field; + } + + SubMapCloneFactory<S> + createCloneFactory(String subMapPrefix, Ice.Properties properties) + { + return new SubMapCloneFactory<S>(new MetricsMap<S>(subMapPrefix, _class, properties, null), _field); + } + + final private Class<S> _class; + final private java.lang.reflect.Field _field; + }; + + MetricsMap(String mapPrefix, Class<T> cl, Ice.Properties props, java.util.Map<String, SubMapFactory<?>> subMaps) + { + _properties = props.getPropertiesForPrefix(mapPrefix); + + _retain = props.getPropertyAsIntWithDefault(mapPrefix + "RetainDetached", 10); + _accept = parseRule(props, mapPrefix + "Accept"); + _reject = parseRule(props, mapPrefix + "Reject"); + _groupByAttributes = new java.util.ArrayList<String>(); + _groupBySeparators = new java.util.ArrayList<String>(); + _class = cl; + + String groupBy = props.getPropertyWithDefault(mapPrefix + "GroupBy", "id"); + if(!groupBy.isEmpty()) + { + String v = ""; + boolean attribute = Character.isLetter(groupBy.charAt(0)) || Character.isDigit(groupBy.charAt(0)); + if(!attribute) + { + _groupByAttributes.add(""); + } + + for(char p : groupBy.toCharArray()) + { + boolean isAlphaNum = Character.isLetter(p) || Character.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.isEmpty()) + { + _subMaps = new java.util.HashMap<String, SubMapCloneFactory<?>>(); + + java.util.List<String> subMapNames = new java.util.ArrayList<String>(); + for(java.util.Map.Entry<String, SubMapFactory<?>> e : subMaps.entrySet()) + { + subMapNames.add(e.getKey()); + String subMapsPrefix = mapPrefix + "Map."; + String subMapPrefix = subMapsPrefix + e.getKey() + '.'; + if(props.getPropertiesForPrefix(subMapPrefix).isEmpty()) + { + if(props.getPropertiesForPrefix(subMapsPrefix).isEmpty()) + { + subMapPrefix = mapPrefix; + } + else + { + continue; // This sub-map isn't configured. + } + } + + _subMaps.put(e.getKey(), e.getValue().createCloneFactory(subMapPrefix, props)); + } + validateProperties(mapPrefix, props, subMapNames); + } + else + { + _subMaps = null; + } + } + + MetricsMap(MetricsMap<T> map) + { + _properties = map._properties; + _groupByAttributes = map._groupByAttributes; + _groupBySeparators = map._groupBySeparators; + _retain = map._retain; + _accept = map._accept; + _reject = map._reject; + _class = map._class; + _subMaps = map._subMaps; + } + + private void + validateProperties(String prefix, Ice.Properties props, java.util.Collection<String> subMaps) + { + if(subMaps.isEmpty()) + { + MetricsAdminI.validateProperties(prefix, props, mapSuffixes); + return; + } + + java.util.List<String> suffixes = new java.util.ArrayList<String>(java.util.Arrays.asList(mapSuffixes)); + for(String s : subMaps) + { + String suffix = "Map." + s + "."; + MetricsAdminI.validateProperties(prefix + suffix, props, mapSuffixes); + suffixes.add(suffix + '*'); + } + MetricsAdminI.validateProperties(prefix, props, suffixes.toArray(new String[suffixes.size()])); + } + + java.util.Map<String, String> + getProperties() + { + return _properties; + } + + synchronized IceMX.Metrics[] + getMetrics() + { + IceMX.Metrics[] metrics = new IceMX.Metrics[_objects.size()]; + int i = 0; + for(Entry e : _objects.values()) + { + metrics[i++] = e.clone(); + } + return metrics; + } + + synchronized IceMX.MetricsFailures[] + getFailures() + { + java.util.List<IceMX.MetricsFailures> failures = new java.util.ArrayList<IceMX.MetricsFailures>(); + for(Entry e : _objects.values()) + { + IceMX.MetricsFailures f = e.getFailures(); + if(f != null) + { + failures.add(f); + } + } + return failures.toArray(new IceMX.MetricsFailures[failures.size()]); + } + + synchronized IceMX.MetricsFailures + getFailures(String id) + { + Entry e = _objects.get(id); + if(e != null) + { + return e.getFailures(); + } + return null; + } + + @SuppressWarnings("unchecked") + public <S extends IceMX.Metrics> SubMap<S> + createSubMap(String subMapName, Class<S> cl) + { + if(_subMaps == null) + { + return null; + } + SubMapCloneFactory<S> factory = (SubMapCloneFactory<S>)_subMaps.get(subMapName); + if(factory != null) + { + return factory.create(); + } + return null; + } + + public Entry + getMatching(IceMX.MetricsHelper<T> helper) + { + // + // Check the accept and reject filters. + // + for(java.util.Map.Entry<String, java.util.regex.Pattern> e : _accept.entrySet()) + { + if(!match(e.getKey(), e.getValue(), helper, false)) + { + return null; + } + } + + for(java.util.Map.Entry<String, java.util.regex.Pattern> e : _reject.entrySet()) + { + if(match(e.getKey(), e.getValue(), helper, false)) + { + return null; + } + } + + // + // Compute the key from the GroupBy property. + // + String key; + try + { + if(_groupByAttributes.size() == 1) + { + key = helper.resolve(_groupByAttributes.get(0)); + } + else + { + StringBuilder os = new StringBuilder(); + java.util.Iterator<String> q = _groupBySeparators.iterator(); + for(String p : _groupByAttributes) + { + os.append(helper.resolve(p)); + if(q.hasNext()) + { + os.append(q.next()); + } + } + key = os.toString(); + } + } + catch(Exception ex) + { + return null; + } + + // + // Lookup the metrics object. + // + synchronized(this) + { + Entry e = _objects.get(key); + if(e == null) + { + try + { + T t = _class.newInstance(); + t.id = key; + e = new Entry(t); + _objects.put(key, e); + } + catch(Exception ex) + { + assert(false); + } + } + return e; + } + } + + private void + detached(Entry entry) + { + if(_retain == 0) + { + return; + } + + synchronized(this) + { + if(_detachedQueue == null) + { + _detachedQueue = new java.util.LinkedList<Entry>(); + } + assert(_detachedQueue.size() <= _retain); + + // Compress the queue by removing entries which are no longer detached. + java.util.Iterator<Entry> p = _detachedQueue.iterator(); + while(p.hasNext()) + { + Entry e = p.next(); + if(e == entry || !e.isDetached()) + { + p.remove(); + } + } + + // If there's still no room, remove the oldest entry (at the front). + if(_detachedQueue.size() == _retain) + { + _objects.remove(_detachedQueue.pollFirst()._object.id); + } + + // Add the entry at the back of the queue. + _detachedQueue.add(entry); + } + } + + private java.util.Map<String, java.util.regex.Pattern> + parseRule(Ice.Properties properties, String name) + { + java.util.Map<String, java.util.regex.Pattern> pats = new java.util.HashMap<String, java.util.regex.Pattern>(); + java.util.Map<String, String> rules = properties.getPropertiesForPrefix(name + '.'); + for(java.util.Map.Entry<String,String> e : rules.entrySet()) + { + pats.put(e.getKey().substring(name.length() + 1), java.util.regex.Pattern.compile(e.getValue())); + } + return pats; + } + + private boolean + match(String attribute, java.util.regex.Pattern regex, IceMX.MetricsHelper<T> helper, boolean reject) + { + String value; + try + { + value = helper.resolve(attribute); + } + catch(Exception ex) + { + return !reject; + } + return regex.matcher(value).matches(); + } + + final private java.util.Map<String, String> _properties; + final private java.util.List<String> _groupByAttributes; + final private java.util.List<String> _groupBySeparators; + final private int _retain; + final private java.util.Map<String, java.util.regex.Pattern> _accept; + final private java.util.Map<String, java.util.regex.Pattern> _reject; + final private Class<T> _class; + + final private java.util.Map<String, Entry> _objects = new java.util.HashMap<String, Entry>(); + final private java.util.Map<String, SubMapCloneFactory<?>> _subMaps; + private java.util.Deque<Entry> _detachedQueue; +};
\ No newline at end of file |