/* * NOTE: This copyright doesnot cover user programs that use HQ program services * by normal system calls through the application program interfaces provided as * part of the Hyperic Plug-in Development Kit or the Hyperic Client Development * Kit - this is merely considered normal use of the program, and doesnot fall * under the heading of "derived work". Copyright (C) [2004, 2005, 2006], * Hyperic, Inc. This file is part of HQ. HQ is free software; you can * redistribute it and/or modify it under the terms version 2 of the GNU General * Public License as published by the Free Software Foundation. This program is * distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU General Public License for more details. You * should have received a copy of the GNU General Public License along with this * program; if not, write to the Free Software Foundation, Inc., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA. */ package org.hyperic.hq.bizapp.server.trigger.conditional; import java.text.MessageFormat; import java.util.Locale; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.hyperic.hq.appdef.shared.AppdefEntityID; import org.hyperic.hq.bizapp.shared.ConditionalTriggerSchema; import org.hyperic.hq.context.Bootstrap; import org.hyperic.hq.events.AbstractEvent; import org.hyperic.hq.events.ActionExecuteException; import org.hyperic.hq.events.EventConstants; import org.hyperic.hq.events.EventTypeException; import org.hyperic.hq.events.InvalidTriggerDataException; import org.hyperic.hq.events.TriggerFiredEvent; import org.hyperic.hq.events.ext.AbstractTrigger; import org.hyperic.hq.events.server.session.AlertConditionEvaluator; import org.hyperic.hq.events.shared.AlertConditionValue; import org.hyperic.hq.events.shared.RegisteredTriggerValue; import org.hyperic.hq.measurement.UnitsConvert; import org.hyperic.hq.measurement.ext.MeasurementEvent; import org.hyperic.hq.measurement.server.session.Measurement; import org.hyperic.hq.measurement.shared.DataManager; import org.hyperic.hq.measurement.shared.MeasurementManager; import org.hyperic.hq.product.MetricValue; import org.hyperic.util.config.ConfigResponse; import org.hyperic.util.config.ConfigSchema; import org.hyperic.util.config.EncodingException; import org.hyperic.util.config.InvalidOptionException; import org.hyperic.util.config.InvalidOptionValueException; import org.hyperic.util.units.FormattedNumber; /** * The ValueChangeTrigger is a simple trigger which fires when a new * MeasurementEvent's value does not match the previous stored */ public class ValueChangeTrigger extends AbstractTrigger implements ConditionalTriggerInterface { static { // Register the trigger/condition ConditionalTriggerInterface.MAP_COND_TRIGGER.put(new Integer(EventConstants.TYPE_CHANGE), ValueChangeTrigger.class); } private static final MessageFormat MESSAGE_FMT = new MessageFormat( "Current value ({0}) differs from previous value ({1})."); private final Object lock = new Object(); private Integer measurementId; private MeasurementEvent last = null; private final Log log = LogFactory.getLog(ValueChangeTrigger.class); private final MeasurementManager measurementManager; private final DataManager dataManager; public ValueChangeTrigger() { this.measurementManager = Bootstrap.getBean(MeasurementManager.class); this.dataManager = Bootstrap.getBean(DataManager.class); } public ValueChangeTrigger(MeasurementManager measurementManager, DataManager dataManager) { this.measurementManager = measurementManager; this.dataManager = dataManager; } /** * @see org.hyperic.hq.events.ext.RegisterableTriggerInterface#getConfigSchema() */ public ConfigSchema getConfigSchema() { return ConditionalTriggerSchema.getConfigSchema(EventConstants.TYPE_CHANGE); } /** * @see org.hyperic.hq.bizapp.server.trigger.conditional.ConditionalTriggerInterface#getConfigResponse(org.hyperic.hq.appdef.shared.AppdefEntityID, * org.hyperic.hq.events.shared.AlertConditionValue) */ public ConfigResponse getConfigResponse(AppdefEntityID id, AlertConditionValue cond) throws InvalidOptionException, InvalidOptionValueException { ConfigResponse resp = new ConfigResponse(); resp.setValue(CFG_ID, String.valueOf(cond.getMeasurementId())); return resp; } /** * @see org.hyperic.hq.events.ext.RegisterableTriggerInterface#init(org.hyperic.hq.events.shared.RegisteredTriggerValue) */ public void init(RegisteredTriggerValue tval, AlertConditionEvaluator alertConditionEvaluator) throws InvalidTriggerDataException { setId(tval.getId()); setAlertConditionEvaluator(alertConditionEvaluator); try { ConfigResponse triggerData = ConfigResponse.decode(getConfigSchema(), tval.getConfig()); measurementId = Integer.valueOf(triggerData.getValue(CFG_ID)); } catch (InvalidOptionException exc) { throw new InvalidTriggerDataException(exc); } catch (InvalidOptionValueException exc) { throw new InvalidTriggerDataException(exc); } catch (EncodingException exc) { throw new InvalidTriggerDataException(exc); } initializeLastValue(); } private void initializeLastValue() { try { Measurement measurement = measurementManager.getMeasurement(measurementId); MetricValue val = dataManager.getLastHistoricalData(measurement); if (val == null) { if (log.isDebugEnabled()) { log.debug("No previous values found for measurement " + measurementId); } return; } this.last = new MeasurementEvent(measurementId, val); } catch (Exception e) { log.error( "Error initializing last value. Changes from previously stored value will not trigger an alert.", e); } } /** * @see org.hyperic.hq.events.ext.RegisterableTriggerInterface#getInterestedEventTypes() */ public Class[] getInterestedEventTypes() { return new Class[] { MeasurementEvent.class }; } /** * @see org.hyperic.hq.events.ext.RegisterableTriggerInterface#getInterestedInstanceIDs(java.lang.Class) */ public Integer[] getInterestedInstanceIDs(Class c) { return new Integer[] { measurementId }; } MeasurementEvent getLast() { return last; } public void setLast(MeasurementEvent last) { this.last = last; } /** * Process an event from the dispatcher. * @param event the Event to process * @throws ActionExecuteException if an action throws an exception */ public void processEvent(AbstractEvent event) throws EventTypeException { // If we didn't fulfill the condition, then don't fire if (!(event instanceof MeasurementEvent)) { throw new EventTypeException("Invalid event type passed, expected MeasurementEvent"); } MeasurementEvent me = (MeasurementEvent) event; if (!me.getInstanceId().equals(measurementId)) { throw new EventTypeException("Invalid instance ID passed (" + me.getInstanceId() + ") expected " + measurementId); } synchronized (lock) { TriggerFiredEvent myEvent = null; if (last == null) { last = me; } else if ((last.getValue().getValue() != me.getValue().getValue()) && (last.getValue().getTimestamp() < me.getValue().getTimestamp())) { // Get ready to fire myEvent = prepareTriggerFiredEvent(event); double values[] = { me.getValue().getValue(), last.getValue().getValue() }; FormattedNumber[] fmtValues = UnitsConvert.convertSame(values, me.getUnits(), Locale.getDefault()); StringBuffer sb = new StringBuffer(); MESSAGE_FMT.format(fmtValues, sb, null); myEvent.setMessage(sb.toString()); last = me; // Update the last reference } if (myEvent != null) { super.fireActions(myEvent); } else { notFired(event); } } } }