/************************************************************************************** * Copyright (C) 2008 EsperTech, Inc. All rights reserved. * * http://esper.codehaus.org * * http://www.espertech.com * * ---------------------------------------------------------------------------------- * * The software in this package is published under the terms of the GPL license * * a copy of which has been included with this distribution in the license.txt file. * **************************************************************************************/ package com.espertech.esper.epl.view; import com.espertech.esper.core.context.util.AgentInstanceContext; import com.espertech.esper.core.service.EPStatementHandleCallback; import com.espertech.esper.core.service.ExtensionServicesContext; import com.espertech.esper.schedule.ScheduleHandleCallback; import com.espertech.esper.schedule.ScheduleSlot; import com.espertech.esper.util.ExecutionPathDebugLog; import com.espertech.esper.util.StopCallback; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * Output condition that is satisfied at the end * of every time interval of a given length. */ public final class OutputConditionTime extends OutputConditionBase implements OutputCondition, StopCallback { private static final boolean DO_OUTPUT = true; private static final boolean FORCE_UPDATE = true; private final AgentInstanceContext context; private final OutputConditionTimeFactory parent; private final ScheduleSlot scheduleSlot; private long msecIntervalSize; private Long currentReferencePoint; private boolean isCallbackScheduled; private EPStatementHandleCallback handle; public OutputConditionTime(OutputCallback outputCallback, AgentInstanceContext context, OutputConditionTimeFactory outputConditionTimeFactory) { super(outputCallback); this.context = context; this.parent = outputConditionTimeFactory; this.scheduleSlot = context.getStatementContext().getScheduleBucket().allocateSlot(); msecIntervalSize = parent.getMsecIntervalSize(); } public final void updateOutputCondition(int newEventsCount, int oldEventsCount) { if ((ExecutionPathDebugLog.isDebugEnabled) && (log.isDebugEnabled())) { log.debug(".updateOutputCondition, " + " newEventsCount==" + newEventsCount + " oldEventsCount==" + oldEventsCount); } if (currentReferencePoint == null) { currentReferencePoint = context.getStatementContext().getSchedulingService().getTime(); } // If we pull the interval from a variable, then we may need to reschedule if (parent.getTimePeriod().hasVariable()) { Double numSeconds = (Double) parent.getTimePeriod().evaluate(null, true, context); if (numSeconds != null) { long newMsecIntervalSize = Math.round(1000 * numSeconds); // reschedule if the interval changed if (newMsecIntervalSize != msecIntervalSize) { if (isCallbackScheduled) { // reschedule context.getStatementContext().getSchedulingService().remove(handle, scheduleSlot); scheduleCallback(); } } } } // Schedule the next callback if there is none currently scheduled if (!isCallbackScheduled) { scheduleCallback(); } } public final String toString() { return this.getClass().getName() + " msecIntervalSize=" + msecIntervalSize; } private void scheduleCallback() { // If we pull the interval from a variable, get the current interval length if (parent.getTimePeriod().hasVariable()) { Double param = (Double) parent.getTimePeriod().evaluate(null, true, context); if (param != null) { msecIntervalSize = Math.round(1000 * param); } } isCallbackScheduled = true; long current = context.getStatementContext().getSchedulingService().getTime(); long afterMSec = computeWaitMSec(current, this.currentReferencePoint, this.msecIntervalSize); if ((ExecutionPathDebugLog.isDebugEnabled) && (log.isDebugEnabled())) { log.debug(".scheduleCallback Scheduled new callback for " + " afterMsec=" + afterMSec + " now=" + current + " currentReferencePoint=" + currentReferencePoint + " msecIntervalSize=" + msecIntervalSize); } ScheduleHandleCallback callback = new ScheduleHandleCallback() { public void scheduledTrigger(ExtensionServicesContext extensionServicesContext) { OutputConditionTime.this.isCallbackScheduled = false; OutputConditionTime.this.outputCallback.continueOutputProcessing(DO_OUTPUT, FORCE_UPDATE); scheduleCallback(); } }; handle = new EPStatementHandleCallback(context.getEpStatementAgentInstanceHandle(), callback); context.getStatementContext().getSchedulingService().add(afterMSec, handle, scheduleSlot); context.getTerminationCallbacks().add(this); } public void stop() { if (handle != null) { context.getStatementContext().getSchedulingService().remove(handle, scheduleSlot); } } /** * Given a current time and a reference time and an interval size, compute the amount of * milliseconds till the next interval. * @param current is the current time * @param reference is the reference point * @param interval is the interval size * @return milliseconds after current time that marks the end of the current interval */ protected static long computeWaitMSec(long current, long reference, long interval) { // Example: current c=2300, reference r=1000, interval i=500, solution s=200 // // int n = ((2300 - 1000) / 500) = 2 // r + (n + 1) * i - c = 200 // // Negative example: current c=2300, reference r=4200, interval i=500, solution s=400 // int n = ((2300 - 4200) / 500) = -3 // r + (n + 1) * i - c = 4200 - 3*500 - 2300 = 400 // long n = (long) ( (current - reference) / (interval * 1f)); if (reference > current) // References in the future need to deduct one window { n--; } long solution = reference + (n + 1) * interval - current; if (solution == 0) { return interval; } return solution; } private static final Log log = LogFactory.getLog(OutputConditionTime.class); }