Java tutorial
/** * Copyright (C) FuseSource, Inc. * http://fusesource.com * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.fusesource.eca.processor; import java.util.List; import java.util.Map; import org.apache.camel.CamelContext; import org.apache.camel.Exchange; import org.apache.camel.impl.ServiceSupport; import org.apache.camel.model.language.ExpressionDefinition; import org.apache.camel.model.language.LanguageExpression; import org.apache.commons.math.stat.descriptive.DescriptiveStatistics; import org.codehaus.jackson.map.ObjectMapper; import org.codehaus.jackson.node.ObjectNode; import org.fusesource.eca.engine.EventHelper; import org.fusesource.eca.eventcache.CacheItem; import org.fusesource.eca.eventcache.EventCache; import org.fusesource.eca.eventcache.EventCacheManager; import org.fusesource.eca.util.PropertyUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Calculates statistics on a Number types in a cache of {@link Exchange}s */ public class StatisticsCalculator extends ServiceSupport { private final static Logger LOG = LoggerFactory.getLogger(StatisticsCalculator.class); private final CamelContext context; private final String cachedId; private final String eventWindow; private EventCacheManager cacheManager; private ExpressionDefinition[] expressionDefinitions; private EventCache<Number> eventCache; private StatisticsType[] statisticsTypes = { StatisticsType.ALL }; private String cacheImplementation = "default"; private String queryString; public StatisticsCalculator(CamelContext camelContext, String cachedId, String eventWindow, String queryString) { this.context = camelContext; this.cachedId = cachedId; this.eventWindow = eventWindow; this.queryString = queryString; } static ExpressionDefinition[] getExpressionDefinitions(CamelContext context, String string) { if (string != null) { String[] expressions = string.split(","); return getExpressionDefinitions(context, expressions); } return null; } static ExpressionDefinition[] getExpressionDefinitions(CamelContext context, String[] expressionStrings) { ExpressionDefinition[] result = null; if (expressionStrings != null) { String languageToUse = "simple"; int start = 0; if (expressionStrings.length > 1) { //see if the first one is a language definition String lang = expressionStrings[0].toLowerCase().trim(); if (context.resolveLanguage(lang) != null) { languageToUse = lang; start = 1; } } result = new ExpressionDefinition[expressionStrings.length - start]; for (int i = start; i < expressionStrings.length; i++) { result[i - start] = new LanguageExpression(languageToUse, expressionStrings[i]); } } return result; } public String calculateStatistics(Exchange exchange) throws Exception { ObjectMapper objectMapper = new ObjectMapper(); ObjectNode objectNode = objectMapper.createObjectNode(); ObjectNode statsNode = objectNode.putObject("statistics"); if (this.expressionDefinitions != null && this.expressionDefinitions.length > 0) { for (ExpressionDefinition expressionDefinition : expressionDefinitions) { ObjectNode expressionNode = statsNode.putObject(expressionDefinition.getExpression()); process(expressionDefinition, exchange, expressionNode); } } else { Map<String, Number> map = getNumbersFromExchange(exchange); if (map != null) { for (Map.Entry<String, Number> entry : map.entrySet()) { ObjectNode valueNode = statsNode.putObject(entry.getKey()); try { process(entry.getValue(), valueNode); } catch (Throwable e) { LOG.error("Failed to process statistics on " + exchange + " for parameter " + entry.getKey() + " = " + entry.getValue(), e); } } } } return objectNode.toString(); } @Override public String toString() { return "statisticsCalculator [" + queryString + "]"; } public String getTraceLabel() { return "statscalc"; } public ExpressionDefinition[] getExpressionDefinitions() { return this.expressionDefinitions; } public String getCacheImplementation() { return cacheImplementation; } public void setCacheImplementation(String cacheImplementation) { this.cacheImplementation = cacheImplementation; } public String getQueryString() { return queryString; } public void setQueryString(String queryString) { this.queryString = queryString; } public String getStatisticsType() { String result = "ALL"; if (statisticsTypes != null && statisticsTypes.length > 0) { result = ""; for (int i = 0; i < statisticsTypes.length; i++) { result += statisticsTypes[i].name(); if (i < (statisticsTypes.length - 1)) { result += ","; } } } return result; } public void setStatisticsType(String type) { if (type != null) { String[] types = type.split(","); this.statisticsTypes = new StatisticsType[types.length]; for (int i = 0; i < types.length; i++) { statisticsTypes[i] = StatisticsType.getType(types[i]); } } if (statisticsTypes == null || statisticsTypes.length == 0 || statisticsTypes[0] == null) { statisticsTypes = new StatisticsType[] { StatisticsType.ALL }; } } @Override protected void doStart() throws Exception { this.cacheManager = EventHelper.getEventCacheManager(context, getCacheImplementation()); this.eventCache = this.cacheManager.getCache(Number.class, this.cachedId, this.eventWindow); this.expressionDefinitions = getExpressionDefinitions(context, getQueryString()); } @Override protected void doStop() throws Exception { this.cacheManager.removeCache(this.cachedId); } protected void process(ExpressionDefinition expressionDefinition, Exchange exchange, ObjectNode statsNode) throws Exception { Number value = getNumberFromExchange(expressionDefinition, exchange); process(value, statsNode); } protected void process(Number value, ObjectNode statsNode) throws Exception { if (statisticsTypes != null) { for (int i = 0; i < statisticsTypes.length; i++) { process(statisticsTypes[i], value, statsNode); } } } protected void process(StatisticsType type, Number value, ObjectNode statsNode) throws Exception { EventCache<Number> cache = this.eventCache; if (value != null && cache != null) { cache.add(value); if (type.equals(StatisticsType.RATE)) { calculateRate(statsNode); } else { List<Number> list = this.eventCache.getWindow(); DescriptiveStatistics descriptiveStatistics = new DescriptiveStatistics(); if (list != null && !list.isEmpty()) { for (Number number : list) { descriptiveStatistics.addValue(number.doubleValue()); } switch (type) { case MEAN: statsNode.put("mean", descriptiveStatistics.getMean()); break; case GEOMETRIC_MEAN: statsNode.put("gemetric mean", descriptiveStatistics.getGeometricMean()); break; case STDDEV: statsNode.put("std-dev", descriptiveStatistics.getStandardDeviation()); break; case MIN: statsNode.put("minimum", descriptiveStatistics.getMin()); break; case MAX: statsNode.put("maximum", descriptiveStatistics.getMax()); break; case SKEWNESS: statsNode.put("skewness", descriptiveStatistics.getSkewness()); break; case KUTOSIS: statsNode.put("kurtosis", descriptiveStatistics.getKurtosis()); break; case VARIANCE: statsNode.put("variance", descriptiveStatistics.getVariance()); break; case COUNT: statsNode.put("count", list.size()); default: statsNode.put("number", descriptiveStatistics.getN()); statsNode.put("mean", descriptiveStatistics.getMean()); statsNode.put("gemetric mean", descriptiveStatistics.getGeometricMean()); statsNode.put("minimum", descriptiveStatistics.getMin()); statsNode.put("maximum", descriptiveStatistics.getMax()); statsNode.put("std-dev", descriptiveStatistics.getStandardDeviation()); statsNode.put("median", descriptiveStatistics.getPercentile(50)); statsNode.put("skewness", descriptiveStatistics.getSkewness()); statsNode.put("kurtosis", descriptiveStatistics.getKurtosis()); statsNode.put("variance", descriptiveStatistics.getVariance()); calculateRate(statsNode); statsNode.put("count", list.size()); } } } } } protected void calculateRate(ObjectNode node) { EventCache<Number> cache = this.eventCache; if (cache != null) { List<CacheItem<Number>> list = cache.getCacheItems(); if (list != null && list.size() > 0) { long start = list.get(0).getTimestamp(); long end = list.get(list.size() - 1).getTimestamp(); long time = (end - start); time = Math.max(1l, time); int rate = (int) ((1000 * list.size()) / time); node.put("rate/sec", rate); } else { node.put("rate/sec", 0); } } } protected Number getNumberFromExchange(ExpressionDefinition expressionDefinition, Exchange exchange) throws Exception { Number value = null; if (expressionDefinition != null) { Object object = expressionDefinition.evaluate(exchange); if (object instanceof Number) { value = (Number) object; } else { value = getNumberFromList(object); } Object payload = exchange.getIn().getBody(); if (value == null && payload != null) { Map<String, Number> map = PropertyUtil.getValues(Number.class, payload); if (map != null && map.isEmpty() == false) { value = map.values().iterator().next(); } } } else { //try and extract something from the Message to monitor Object payload = exchange.getIn().getBody(); if (payload != null) { Map<String, Number> map = PropertyUtil.getValues(Number.class, payload); if (map != null && map.isEmpty() == false) { value = map.values().iterator().next(); } } } return value; } protected Map<String, Number> getNumbersFromExchange(Exchange exchange) throws Exception { Map<String, Number> result = null; //try and extract something from the Message to monitor Object payload = exchange.getIn().getBody(); if (payload != null) { result = PropertyUtil.getValues(Number.class, payload); } return result; } private Number getNumberFromList(Object value) { //some expressions return a value in a List - e.g. sql if (value instanceof Number) { return (Number) value; } if (value instanceof List) { List list = (List) value; if (list.isEmpty() == false) { return getNumberFromList(list.get(0)); } return null; } return null; } }