package com.orbitz.monitoring.lib; import com.orbitz.monitoring.api.Decomposer; import com.orbitz.monitoring.api.InheritableStrategy; import com.orbitz.monitoring.api.MonitorProcessor; import com.orbitz.monitoring.api.MonitorProcessorFactory; import com.orbitz.monitoring.api.MonitoringEngine; import com.orbitz.monitoring.api.MonitoringLevel; import com.orbitz.monitoring.api.engine.StackBasedInheritableStrategy; import com.orbitz.monitoring.api.monitor.EventMonitor; import com.orbitz.monitoring.lib.decomposer.AttributeDecomposer; import com.orbitz.monitoring.lib.factory.SimpleMonitorProcessorFactory; 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; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.TimerTask; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; /** * Allows {@link MonitoringEngine} to be manageable. * @author Doug Barth * @@org.springframework.jmx.export.metadata.ManagedResource( * description="Management interface for ERMA MonitoringEngine") */ @ManagedResource(description = "Management interface for ERMA MonitoringEngine") // TODO: Remove the Javadoc annotation public class BaseMonitoringEngineManager { /** * Set into the {@link MonitoringEngine} using * {@link MonitoringEngine#setStartupRunnable(Runnable)} */ protected Runnable startupRunnable; private final Decomposer decomposer; private final MonitorProcessorFactory factory; private final Logger log = Logger.getLogger(BaseMonitoringEngineManager.class); private InheritableStrategy inheritableStrategy; private boolean monitoringEnabled = true; private ScheduledExecutorService scheduledExecutor; private Map<Integer, Collection<TimerTask>> timerTasks; /** * Creates a {@link BaseMonitoringEngineManager} with an empty * {@link SimpleMonitorProcessorFactory} */ public BaseMonitoringEngineManager() { this(new SimpleMonitorProcessorFactory(null), null); } /** * Creates a {@link BaseMonitoringEngineManager} * @param factory used to create {@link MonitorProcessor MonitorProcessors} */ public BaseMonitoringEngineManager(final MonitorProcessorFactory factory) { this(factory, null); } /** * Creates a {@link BaseMonitoringEngineManager} * @param factory used to create {@link MonitorProcessor MonitorProcessors} * @param decomposer used to make properties of monitors {@link java.io.Serializable} */ public BaseMonitoringEngineManager(final MonitorProcessorFactory factory, Decomposer decomposer) { if (decomposer == null) { decomposer = new AttributeDecomposer(); } this.factory = factory; this.decomposer = decomposer; this.inheritableStrategy = new StackBasedInheritableStrategy(); } /** * Assign a MonitoringLevel to a MonitorProcessor. MonitorProcessor levels override ProcessGroup * levels for every group that contains the processor instance. * * @param name MonitorProcessor name * @param levelStr monitoring level * * @@org.springframework.jmx.export.metadata.ManagedOperation( * description="Sets a monitoring level on a MonitorProcessor") * @@org.springframework.jmx.export.metadata.ManagedOperationParameter (index=0, name="name", * description="processor name as configured in spring bean definition"xi) * @@org.springframework.jmx.export.metadata.ManagedOperationParameter (index=1, name="levelStr", * description="Monitoring level to apply to processor") */ @ManagedOperation(description = "Sets a monitoring level on a MonitorProcessor") @ManagedOperationParameters({ @ManagedOperationParameter(name = "name", description = "processor name as configured in spring bean definition"), @ManagedOperationParameter(name = "levelStr", description = "Monitoring level to apply to processor")}) // TODO: Remove the Javadoc annotation public void addLevelForProcessor(final String name, final String levelStr) { if (name == null) { throw new IllegalArgumentException("processor name cannot be null"); } else if (!MonitoringLevel.isValidLevelStr(levelStr)) { throw new IllegalArgumentException("levelStr must match an existing MonitoringLevel"); } MonitorProcessor[] processors = factory.getAllProcessors(); for (int i = 0; i < processors.length; i++) { MonitorProcessor processor = processors[i]; if (name.equalsIgnoreCase(processor.getName())) { MonitoringEngine.getInstance().addProcessorLevel(name, MonitoringLevel.toLevel(levelStr)); log.info("Changed Processor level: " + name + " -> " + levelStr); return;// two processors should not have same name } } } /** * Determines what a {@link com.orbitz.monitoring.api.Monitor} inherits from its parent(s) * @return the strategy */ public InheritableStrategy getInheritableStrategy() { return inheritableStrategy; } /** * Gets the value that is used to determine whether monitoring will be enabled for the * {@link MonitoringEngine} when {@link #startup()} is called. * @return true if monitoring should be enabled for the {@link MonitoringEngine}, false otherwise */ public boolean getMonitoringEnabled() { return monitoringEnabled; } /** * Gets the override monitor levels by calling * {@link MonitoringEngine#getOverrideMonitorLevelsListing()} * @return description of override monitor levels map * @see MonitoringEngine#getOverrideMonitorLevelsListing() * @@org.springframework.jmx.export.metadata.ManagedAttribute( * description="Gets a view into monitor level overrides") */ @ManagedAttribute(description = "Gets a view into monitor level overrides") // TODO: Remove the Javadoc annotation public String getOverrideMonitorLevelsListing() { return MonitoringEngine.getInstance().getOverrideMonitorLevelsListing(); } /** * Gets the override processor levels by calling * {@link MonitoringEngine#getOverrideProcessorLevelsListing()} * @return description of override processor levels map * @see MonitoringEngine#getOverrideProcessorLevelsListing() * @@org.springframework.jmx.export.metadata.ManagedAttribute( * description="Gets a view into processor level overrides") */ @ManagedAttribute(description = "Gets a view into processor level overrides") public String getOverrideProcessorLevelsListing() { return MonitoringEngine.getInstance().getOverrideProcessorLevelsListing(); } /** * Gets the timer tasks that will be scheduled to run every minute when {@link #startup()} is * called.<br> * <i>Note that any timer tasks scheduled to run at any frequency other than one minute will not * be returned.</i> * @return the minutely timer tasks */ public Collection<TimerTask> getTimerTasks() { if (timerTasks != null && timerTasks.containsKey(new Integer(60000))) { return timerTasks.get(new Integer(60000)); } return Collections.emptyList(); } /** * Gets all timer tasks<br> * <ul> * <li> * <b>60000</b> * <ul> * <li>Task run every minute</li> * <li>Task run every minute</li> * <li>Task run every minute</li> * </ul> * </li> * <li> * <b>1000</b> * <ul> * <li>Task run every second</li> * </ul> * </li> * <li> * <b>3600000</b> * <ul> * <li>Task run every hour</li> * </ul> * </li> * </ul> * @return a map of timer tasks in which the keys are the frequencies, in milliseconds, at which * each task is run and values are collections of tasks to run at those frequencies. */ public Map<Integer, Collection<TimerTask>> getTimerTasksMap() { return timerTasks; } /** * Restarts the {@link MonitoringEngine} * @see MonitoringEngine#restart() */ public void reload() { EventMonitor monitor = new EventMonitor("MonitoringEngineManager.lifecycle", MonitoringLevel.ESSENTIAL); monitor.set("eventType", "reload"); monitor.fire(); MonitoringEngine.getInstance().restart(); } /** * Sets the strategy that determines whether {@link com.orbitz.monitoring.api.Monitor} attributes * are inherited by their children * @param inheritableStrategy the strategy */ public void setInheritableStrategy(final InheritableStrategy inheritableStrategy) { this.inheritableStrategy = inheritableStrategy; } /** * Sets the value that determines whether the {@link MonitoringEngine} will be enabled when * {@link #startup()} is called. * @param monitoringEnabled true to enable monitoring, false to cause the {@link MonitoringEngine} * to ignore all monitors * @see MonitoringEngine#setMonitoringEnabled(boolean) */ public void setMonitoringEnabled(final boolean monitoringEnabled) { this.monitoringEnabled = monitoringEnabled; } /** * Sets the runnable that will be set using {@link MonitoringEngine#setStartupRunnable(Runnable)} * when {@link #startup()} is called. * @param startupRunnable the startup runnable */ public void setStartupRunnable(final Runnable startupRunnable) { this.startupRunnable = startupRunnable; } /** * Sets timer tasks that will be executed every minute<br> * <i>Note that this will overwrite existing timer tasks.</i> * @param timerTasks timer tasks to set */ public void setTimerTasks(final Collection<TimerTask> timerTasks) { setTimerTasksMap(Collections.singletonMap(new Integer(60000), timerTasks)); } /** * Sets the timer tasks * @param timerTasks the map of timer tasks, in which keys are the frequencies at which tasks are * executed and keys are collections of the tasks to execute at those frequencies * @see #getTimerTasksMap() */ public void setTimerTasksMap(final Map<Integer, Collection<TimerTask>> timerTasks) { this.timerTasks = timerTasks; } /** * Stops all {@link #getTimerTasks() timer tasks} and {@link MonitoringEngine#shutdown() shuts the * monitoring engine down}. */ public void shutdown() { scheduledExecutor.shutdown(); EventMonitor monitor = new EventMonitor("MonitoringEngineManager.lifecycle", MonitoringLevel.ESSENTIAL); monitor.set("eventType", "shutdown"); monitor.fire(); MonitoringEngine.getInstance().shutdown(); } /** * Sets all properties of the {@link MonitoringEngine} that have been configured, such as * {@link #getMonitoringEnabled()} and {@link #getInheritableStrategy()}, then starts it. After * the {@link MonitoringEngine} is started, schedules all {@link #getTimerTasks() timer tasks}. */ public void startup() { MonitoringEngine.getInstance().setMonitoringEnabled(monitoringEnabled); MonitoringEngine.getInstance().setProcessorFactory(factory); MonitoringEngine.getInstance().setDecomposer(decomposer); MonitoringEngine.getInstance().setInheritableStrategy(inheritableStrategy); MonitoringEngine.getInstance().setStartupRunnable(startupRunnable); if (timerTasks == null) { timerTasks = Collections.emptyMap(); } MonitoringEngine.getInstance().startup(); EventMonitor monitor = new EventMonitor("MonitoringEngineManager.lifecycle", MonitoringLevel.ESSENTIAL); monitor.set("eventType", "startup"); monitor.fire(); scheduledExecutor = Executors.newSingleThreadScheduledExecutor(); for (Entry<Integer, Collection<TimerTask>> entry : timerTasks.entrySet()) { long millis = entry.getKey(); for (Runnable task : entry.getValue()) { scheduledExecutor.scheduleAtFixedRate(task, millis, millis, TimeUnit.MILLISECONDS); } } } /** * Sets a monitoring level in the {@link MonitoringEngine} * @param nameStartsWith either the full name of the monitor or a partial string that will be used * to match on the beginning of the monitor name * @param levelStr string representation of the monitoring level to set * @see MonitoringEngine#addMonitorLevel(String, MonitoringLevel) * @@org.springframework.jmx.export.metadata.ManagedOperation( * description="Sets the monitoring level for the monitor(s)") * @@org.springframework.jmx.export.metadata.ManagedOperationParameter (index=0, * name="nameStartsWith", * description="Apply to all monitor names that start with the given string") * @@org.springframework.jmx.export.metadata.ManagedOperationParameter (index=1, name="levelStr", * description="Monitoring level to apply to monitor(s)") */ @ManagedOperation(description = "Sets the monitoring level for the monitor(s)") @ManagedOperationParameters({ @ManagedOperationParameter(name = "nameStartsWith", description = "Apply to all monitor names that start with the given string"), @ManagedOperationParameter(name = "levelStr", description = "Monitoring level to apply to monitor(s)")}) // TODO: Remove the Javadoc annotation public void updateLevelForMonitor(final String nameStartsWith, final String levelStr) { if (nameStartsWith == null) { throw new IllegalArgumentException("nameStartsWith cannot be null"); } else if (!MonitoringLevel.isValidLevelStr(levelStr)) { throw new IllegalArgumentException("levelStr must match an existing MonitoringLevel"); } MonitoringLevel level = MonitoringLevel.toLevel(levelStr); MonitoringEngine.getInstance().addMonitorLevel(nameStartsWith, level); if (log.isInfoEnabled()) { log.info("Added: " + nameStartsWith + " -> " + levelStr + " to map of monitor level overrides."); } } }