summaryrefslogtreecommitdiff
path: root/java/src/IceInternal/MetricsMap.java
diff options
context:
space:
mode:
Diffstat (limited to 'java/src/IceInternal/MetricsMap.java')
-rw-r--r--java/src/IceInternal/MetricsMap.java530
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