/*******************************************************************************
* Copyright (c) 2006-2010 eBay Inc. All Rights Reserved.
* 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
*******************************************************************************/
package org.ebayopensource.turmeric.runtime.common.impl.internal.monitoring;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import org.ebayopensource.turmeric.runtime.common.exceptions.ServiceException;
import org.ebayopensource.turmeric.runtime.common.impl.internal.pipeline.BaseMessageContextImpl;
import org.ebayopensource.turmeric.runtime.common.impl.utils.LogManager;
import org.ebayopensource.turmeric.runtime.common.monitoring.MetricCategory;
import org.ebayopensource.turmeric.runtime.common.monitoring.MetricClassifier;
import org.ebayopensource.turmeric.runtime.common.monitoring.MetricDef;
import org.ebayopensource.turmeric.runtime.common.monitoring.MetricId;
import org.ebayopensource.turmeric.runtime.common.monitoring.MetricsCollector;
import org.ebayopensource.turmeric.runtime.common.monitoring.MonitoringLevel;
import org.ebayopensource.turmeric.runtime.common.monitoring.value.BaseMetricValue;
import org.ebayopensource.turmeric.runtime.common.monitoring.value.MetricComponentType;
import org.ebayopensource.turmeric.runtime.common.monitoring.value.MetricComponentValue;
import org.ebayopensource.turmeric.runtime.common.monitoring.value.MetricValue;
import org.ebayopensource.turmeric.runtime.common.monitoring.value.MetricValueAggregator;
import org.ebayopensource.turmeric.runtime.common.pipeline.MessageContext;
import org.ebayopensource.turmeric.runtime.common.pipeline.MessageContextAccessor;
import org.ebayopensource.turmeric.runtime.common.types.SOAConstants;
import org.ebayopensource.turmeric.runtime.common.types.SOAHeaders;
/**
* @author wdeng, ichernyshev
*/
final class MetricValueAggregatorImpl extends BaseMetricValue implements MetricValueAggregator {
private static final String METRIC_CLASSIFIER_PROP = "org.ebayopensource.turmeric.runtime.MetricClassifier";
private static final MetricClassifier UNKNOWN_CLASSIFIER = new MetricClassifier("Unknown", "Unknown", "Unknown");
/*
* By default, we will use the Usecase, DC based classifier. However, user can
* specify a custom classifier, by providing a factory for the MetricValue.
*/
private final MetricDef m_metricDef;
private final boolean m_isNormalLevel;
private final MetricsCollector m_collector;
private HashMap<MetricClassifier, MetricValue> m_valueByClassifier;
private MetricValue m_firstValue;
public static MetricValueAggregatorImpl create(MetricId id,
MetricDef metricDef, MetricsCollector collector)
{
if (metricDef == null || id == null) {
throw new NullPointerException();
}
MetricValue sampleValue = metricDef.getValueFactory().create(id);
List<MetricComponentType> types = sampleValue.getAllComponentsTypes();
return new MetricValueAggregatorImpl(id, types, metricDef, collector);
}
private MetricValueAggregatorImpl(MetricId id, List<MetricComponentType> types,
MetricDef metricDef, MetricsCollector collector)
{
super(id, types);
m_metricDef = metricDef;
m_isNormalLevel = (metricDef.getLevel() == MonitoringLevel.NORMAL);
m_collector = collector;
m_valueByClassifier = new HashMap<MetricClassifier,MetricValue>();
}
public final boolean isEnabled() {
if (m_isNormalLevel) {
return true;
}
MonitoringLevel effectiveLevel = m_collector.getMonitoringLevel(getMetricId().getAdminName());
return effectiveLevel.ordinal() >= m_metricDef.getLevel().ordinal();
}
public MetricDef getMetricDef() {
return m_metricDef;
}
public MetricCategory getCategory() {
return m_metricDef.getCategory();
}
public MonitoringLevel getLevel() {
return m_metricDef.getLevel();
}
@Override
public synchronized MetricValue deepCopy(boolean isReadOnly) {
MetricValueAggregatorImpl result = (MetricValueAggregatorImpl)super.deepCopy(isReadOnly);
HashMap<MetricClassifier,MetricValue> valuesSrc = m_valueByClassifier;
HashMap<MetricClassifier,MetricValue> valuesDst =
new HashMap<MetricClassifier,MetricValue>(valuesSrc.size());
for (Map.Entry<MetricClassifier,MetricValue> e: valuesSrc.entrySet()) {
MetricValue value2 = e.getValue().deepCopy(isReadOnly);
valuesDst.put(e.getKey(), value2);
}
result.m_valueByClassifier = valuesDst;
return result;
}
@Override
public synchronized void addOtherValue(MetricValue other, boolean isPositive) {
checkUpdateable();
MetricValueAggregatorImpl other2 = (MetricValueAggregatorImpl)other;
HashMap<MetricClassifier,MetricValue> otherValues = other2.m_valueByClassifier;
for (Map.Entry<MetricClassifier,MetricValue> e: m_valueByClassifier.entrySet()) {
MetricClassifier classifier = e.getKey();
BaseMetricValue newValue = (BaseMetricValue)e.getValue();
MetricValue otherValue = otherValues.get(classifier);
if (otherValue != null) {
newValue.addOtherValue(otherValue, isPositive);
}
}
}
public MetricValue getTotalValue() {
BaseMetricValue result = null;
for (MetricValue value: m_valueByClassifier.values()) {
if (result != null) {
result.addOtherValue(value, true);
} else {
if (m_valueByClassifier.size() == 1) {
return value.deepCopy(true);
}
// there is more than 1 object, we will call add,
// so no need to create a copy here
result = (BaseMetricValue)value.deepCopy(false);
}
}
if (result != null) {
return result.deepCopy(true);
}
return null;
}
@Override
public MetricComponentValue[] getValues() {
throw new UnsupportedOperationException("MetricValueAggregatorImpl does not support getValues call");
}
public Collection<MetricClassifier> getClassifiers() {
// no need to synchronize as people are handed out copies only
return m_valueByClassifier.keySet();
}
public MetricValue getValue(MetricClassifier classifier) {
return m_valueByClassifier.get(classifier);
}
@Override
public void update(MetricValue value) {
if (! isEnabled()) return;
MetricClassifier classifier = getClassifierFromContext();
update(classifier, value);
}
@Override
public void update(int value) {
if (! isEnabled()) return;
MetricClassifier classifier = getClassifierFromContext();
update(classifier, value);
}
@Override
public void update(long value) {
if (! isEnabled()) return;
MetricClassifier classifier = getClassifierFromContext();
update(classifier, value);
}
@Override
public void update(float value) {
if (! isEnabled()) return;
MetricClassifier classifier = getClassifierFromContext();
update(classifier, value);
}
@Override
public void update(double value) {
if (! isEnabled()) return;
MetricClassifier classifier = getClassifierFromContext();
update(classifier, value);
}
public void update(MessageContext ctx, MetricValue value) {
if (! isEnabled()) return;
MetricClassifier classifier = getClassifierFromContext(ctx);
update(classifier, value);
}
public void update(MessageContext ctx, int value) {
if (! isEnabled()) return;
MetricClassifier classifier = getClassifierFromContext(ctx);
update(classifier, value);
}
public void update(MessageContext ctx, long value) {
if (! isEnabled()) return;
MetricClassifier classifier = getClassifierFromContext(ctx);
update(classifier, value);
}
public void update(MessageContext ctx, float value) {
if (! isEnabled()) return;
MetricClassifier classifier = getClassifierFromContext(ctx);
update(classifier, value);
}
public void update(MessageContext ctx, double value) {
if (! isEnabled()) return;
MetricClassifier classifier = getClassifierFromContext(ctx);
update(classifier, value);
}
private synchronized void update(MetricClassifier classifier, MetricValue newValue) {
checkUpdateable();
MetricValue classifiedValue = getMetricValueForClassifier(classifier);
classifiedValue.update(newValue);
}
private synchronized void update(MetricClassifier classifier, int value) {
checkUpdateable();
MetricValue classifiedValue = getMetricValueForClassifier(classifier);
classifiedValue.update(value);
}
private synchronized void update(MetricClassifier classifier, long value) {
checkUpdateable();
MetricValue classifiedValue = getMetricValueForClassifier(classifier);
classifiedValue.update(value);
}
private synchronized void update(MetricClassifier classifier, float value) {
checkUpdateable();
MetricValue classifiedValue = getMetricValueForClassifier(classifier);
classifiedValue.update(value);
}
private synchronized void update(MetricClassifier classifier, double value) {
checkUpdateable();
MetricValue classifiedValue = getMetricValueForClassifier(classifier);
classifiedValue.update(value);
}
/**
* Resets the values, to be called by Ops manually
*/
public synchronized void reset() {
checkUpdateable();
m_valueByClassifier.clear();
}
private MetricValue getMetricValueForClassifier(MetricClassifier classifier) {
MetricValue value = m_valueByClassifier.get(classifier);
if (value == null) {
value = m_metricDef.getValueFactory().create(getMetricId());
if (value == null) {
throw new NullPointerException();
}
if (m_firstValue != null) {
if (m_firstValue.getClass() != value.getClass()) {
throw new IllegalArgumentException(
m_metricDef.getValueFactory().getClass().getName() +
" returned two different value types: " + value.getClass().getName() +
" and " + m_firstValue.getClass().getName());
}
} else {
m_firstValue = value;
}
m_valueByClassifier.put(classifier, value);
}
return value;
}
private static MetricClassifier getClassifierFromContext() {
BaseMessageContextImpl ctx = (BaseMessageContextImpl)MessageContextAccessor.getContext();
return getClassifierFromContext(ctx);
}
private static MetricClassifier getClassifierFromContext(MessageContext ctx) {
return getClassifierFromContext((BaseMessageContextImpl)ctx);
}
private static MetricClassifier getClassifierFromContext(BaseMessageContextImpl ctx) {
/*
* Look in the MessageContext, find the USECASE_NAME and DC and
* look up in the map m_ClassifierBasedValues and
* update the respective MetricValue.
* The logic is similar for all update methods.
*/
if (ctx == null) {
return UNKNOWN_CLASSIFIER;
}
MetricClassifier result = (MetricClassifier)ctx.getProperty(METRIC_CLASSIFIER_PROP);
try {
String usecase = ctx.getRequestMessage().getTransportHeader(SOAHeaders.USECASE_NAME);
String knownUsecase = result == null ? null : result.getUseCase();
if (knownUsecase != null && !SOAConstants.DEFAULT_USE_CASE.equals(knownUsecase)
&& knownUsecase.equals(usecase)) {
return result;
}
String sourceDc = ctx.getClientAddress().getDataCenter();
String targetDc = ctx.getServiceAddress().getDataCenter();
if (usecase == null) {
usecase = SOAConstants.DEFAULT_USE_CASE;
}
if (targetDc == null) {
targetDc = "Unknown";
}
if (sourceDc == null) {
sourceDc = "Unknown";
}
result = new MetricClassifier(usecase, sourceDc, targetDc);
} catch (ServiceException e) {
LogManager.getInstance(MetricValueAggregatorImpl.class).log(Level.WARNING,
"Unable to build SOA Metrics Classifier: " + e.toString(), e);
result = UNKNOWN_CLASSIFIER;
}
ctx.setSystemProperty(METRIC_CLASSIFIER_PROP, result);
return result;
}
}