package com.orbitz.monitoring.lib.factory;
import com.orbitz.monitoring.api.Monitor;
import com.orbitz.monitoring.api.MonitoringEngine;
import com.orbitz.monitoring.api.MonitorProcessor;
import com.orbitz.monitoring.api.MonitoringLevel;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import java.util.Collections;
import java.util.List;
import org.apache.commons.jexl.Expression;
import org.apache.commons.jexl.ExpressionFactory;
import org.apache.commons.jexl.JexlContext;
import org.apache.commons.jexl.JexlHelper;
import org.apache.log4j.Logger;
import org.springframework.jmx.export.annotation.ManagedAttribute;
import org.springframework.jmx.export.annotation.ManagedOperation;
import org.springframework.jmx.export.annotation.ManagedOperationParameter;
import org.springframework.jmx.export.annotation.ManagedOperationParameters;
import org.springframework.jmx.export.annotation.ManagedResource;
/**
* An object that contains the configuration for what processors should be called for which
* monitors.
*/
@ManagedResource(description = "ProcessGroup can be enabled/disabled and MonitoringLevel adjusted")
public class ProcessGroup {
private static final Logger log = Logger.getLogger(ProcessGroup.class);
private MonitoringLevel _monitoringLevel = MonitoringLevel.INFO;
private boolean _active = true;
private Expression _appliesExpression;
private final List<MonitorProcessor> _processors;
public ProcessGroup(final MonitorProcessor processor) {
this(Lists.newArrayList(processor));
}
public ProcessGroup(final MonitorProcessor[] processors) {
_processors = Lists.newArrayList(processors);
}
public ProcessGroup(final List<MonitorProcessor> processors) {
_processors = processors;
}
/**
* Returns the list of processors within this ProcessGroup that apply for the MonitoringLevel of
* the given monitor.
*
* @param monitor Monitor instance ready for processing
* @return list of MonitorProcessors
*/
public Iterable<MonitorProcessor> getProcessorsFor(final Monitor monitor) {
if (!isActive()) {
return Collections.emptyList();
}
final MonitoringLevel monitorLevel = monitor.getLevel();
// Use a filter here because it creates a view of the existing list instead of making a copy
Iterable<MonitorProcessor> processorsForMonitor = Iterables.filter(_processors,
new Predicate<MonitorProcessor>() {
public boolean apply(final MonitorProcessor processor) {
MonitoringLevel processorLevel = findMonitoringEngine().getProcessorLevel(
processor.getName());
if (processorLevel != null) {
return monitorLevel.hasHigherOrEqualPriorityThan(processorLevel);
} else {
return monitorLevel.hasHigherOrEqualPriorityThan(_monitoringLevel);
}
}
});
if (processorsForMonitor.iterator().hasNext()) {
if (!matchesExpressionFor(monitor)) {
return Collections.emptyList();
}
}
return processorsForMonitor;
}
@VisibleForTesting
MonitoringEngine findMonitoringEngine() {
return MonitoringEngine.getInstance();
}
/**
* appliesTo will determine if this ProcessGroup should have the monitor.
* @param monitor being processed
* @return true if the monitor will be handled by this process group, else false
*/
@VisibleForTesting
boolean matchesExpressionFor(final Monitor monitor) {
boolean applies = true;
if (_appliesExpression != null) {
JexlContext context = JexlHelper.createContext();
context.getVars().put("m", monitor);
context.getVars().putAll(monitor.getAll());
try {
Object result = _appliesExpression.evaluate(context);
if (result != null && result instanceof Boolean) {
Boolean expressionResult = (Boolean)result;
applies = expressionResult.booleanValue();
} else {
applies = false;
}
} catch (Exception e) {
log.debug("Exception while applying expression: ", e);
applies = false;
}
}
return applies;
}
public void setExpression(final String expressionString) {
Expression expression = null;
if (expressionString != null) {
try {
expression = ExpressionFactory.createExpression(expressionString);
} catch (Exception e) {
log.error("Error setting expression: ", e);
}
}
_appliesExpression = expression;
}
@ManagedAttribute(description = "Returns true if this process group is enabled")
public boolean isActive() {
return _active;
}
@ManagedAttribute(description = "Set to true/false to activate/deactivate the process group")
public void setActive(final boolean active) {
_active = active;
log.info(this.toString() + (active ? " activated" : " deactivated"));
}
/**
* Gets all processors
* @return the processors
* @deprecated use {@link #getAllProcessors()} instead
*/
@Deprecated
public MonitorProcessor[] getProcessors() {
return _processors.toArray(new MonitorProcessor[0]);
}
/**
* Gets all processors
* @return the processors
*/
public List<MonitorProcessor> getAllProcessors() {
return _processors;
}
@ManagedAttribute(
description = "get the string representation of the monitoring level for this process group")
public String getMonitoringLevel() {
return _monitoringLevel.toString();
}
@ManagedOperation(description = "Set the monitoring level for this process group")
@ManagedOperationParameters({@ManagedOperationParameter(name = "levelString",
description = "new MonitoringLevel to apply")})
public void updateMonitoringLevel(final String levelString) {
if (!MonitoringLevel.isValidLevelStr(levelString)) {
throw new IllegalArgumentException("levelString must match an existing MonitoringLevel");
}
_monitoringLevel = MonitoringLevel.toLevel(levelString);
log.info(this.toString() + " -> " + levelString);
}
/**
* Used for spring wiring, just wraps the above runtime method
* @param levelString new MonitoringLevel to apply
*/
public void setMonitoringLevel(final String levelString) {
updateMonitoringLevel(levelString);
}
}