/*
***************************************************************************************
* Copyright (C) 2006 EsperTech, Inc. All rights reserved. *
* http://www.espertech.com/esper *
* 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.view.window;
import com.espertech.esper.client.EventBean;
import com.espertech.esper.collection.ViewUpdatedCollection;
import com.espertech.esper.core.context.util.AgentInstanceViewFactoryChainContext;
import com.espertech.esper.epl.agg.service.AggregationServiceAggExpressionDesc;
import com.espertech.esper.epl.agg.service.AggregationServiceFactoryDesc;
import com.espertech.esper.epl.expression.core.ExprEvaluator;
import com.espertech.esper.event.arr.ObjectArrayEventBean;
import com.espertech.esper.metrics.instrumentation.InstrumentationHelper;
import com.espertech.esper.view.View;
import com.espertech.esper.view.ViewDataVisitor;
import com.espertech.esper.view.ViewFactory;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;
/**
* This view is a moving window extending the into the past until the expression passed to it returns false.
*/
public class ExpressionBatchView extends ExpressionViewBase {
private final ExpressionBatchViewFactory dataWindowViewFactory;
protected final Set<EventBean> window = new LinkedHashSet<EventBean>();
protected EventBean[] lastBatch;
protected long newestEventTimestamp;
protected long oldestEventTimestamp;
protected EventBean oldestEvent;
protected EventBean newestEvent;
public ExpressionBatchView(ExpressionBatchViewFactory dataWindowViewFactory,
ViewUpdatedCollection viewUpdatedCollection,
ExprEvaluator expiryExpression,
AggregationServiceFactoryDesc aggregationServiceFactoryDesc,
ObjectArrayEventBean builtinEventProps,
Set<String> variableNames,
AgentInstanceViewFactoryChainContext agentInstanceContext) {
super(viewUpdatedCollection, expiryExpression, aggregationServiceFactoryDesc, builtinEventProps, variableNames, agentInstanceContext);
this.dataWindowViewFactory = dataWindowViewFactory;
}
public String getViewName() {
return dataWindowViewFactory.getViewName();
}
public View cloneView() {
return dataWindowViewFactory.makeView(agentInstanceContext);
}
/**
* Returns true if the window is empty, or false if not empty.
*
* @return true if empty
*/
public boolean isEmpty() {
return window.isEmpty();
}
public void scheduleCallback() {
boolean fireBatch = evaluateExpression(null, window.size());
if (fireBatch) {
expire(window.size());
}
}
public void update(EventBean[] newData, EventBean[] oldData) {
if (InstrumentationHelper.ENABLED) {
InstrumentationHelper.get().qViewProcessIRStream(this, dataWindowViewFactory.getViewName(), newData, oldData);
}
boolean fireBatch = false;
// remove points from data window
if (oldData != null) {
for (EventBean anOldData : oldData) {
window.remove(anOldData);
}
if (aggregationService != null) {
aggregationService.applyLeave(oldData, null, agentInstanceContext);
}
if (!window.isEmpty()) {
oldestEvent = window.iterator().next();
} else {
oldestEvent = null;
}
fireBatch = evaluateExpression(null, window.size());
}
// add data points to the window
int numEventsInBatch = -1;
if (newData != null && newData.length > 0) {
if (window.isEmpty()) {
oldestEventTimestamp = agentInstanceContext.getStatementContext().getSchedulingService().getTime();
}
newestEventTimestamp = agentInstanceContext.getStatementContext().getSchedulingService().getTime();
if (oldestEvent == null) {
oldestEvent = newData[0];
}
for (EventBean newEvent : newData) {
window.add(newEvent);
if (aggregationService != null) {
aggregationService.applyEnter(new EventBean[]{newEvent}, null, agentInstanceContext);
}
newestEvent = newEvent;
if (!fireBatch) {
fireBatch = evaluateExpression(newEvent, window.size());
if (fireBatch && !dataWindowViewFactory.isIncludeTriggeringEvent()) {
numEventsInBatch = window.size() - 1;
}
}
}
}
// may fire the batch
if (fireBatch) {
expire(numEventsInBatch);
}
if (InstrumentationHelper.ENABLED) {
InstrumentationHelper.get().aViewProcessIRStream();
}
}
// Called based on schedule evaluation registered when a variable changes (new data is null).
// Called when new data arrives.
public void expire(int numEventsInBatch) {
if (numEventsInBatch == window.size() || numEventsInBatch == -1) {
EventBean[] batchNewData = window.toArray(new EventBean[window.size()]);
if (viewUpdatedCollection != null) {
viewUpdatedCollection.update(batchNewData, lastBatch);
}
// post
if (batchNewData != null || lastBatch != null) {
if (InstrumentationHelper.ENABLED) {
InstrumentationHelper.get().qViewIndicate(this, dataWindowViewFactory.getViewName(), batchNewData, lastBatch);
}
updateChildren(batchNewData, lastBatch);
if (InstrumentationHelper.ENABLED) {
InstrumentationHelper.get().aViewIndicate();
}
}
// clear
window.clear();
lastBatch = batchNewData;
if (aggregationService != null) {
aggregationService.clearResults(agentInstanceContext);
}
oldestEvent = null;
newestEvent = null;
} else {
EventBean[] batchNewData = new EventBean[numEventsInBatch];
Iterator<EventBean> it = window.iterator();
for (int i = 0; i < batchNewData.length; i++) {
batchNewData[i] = it.next();
it.remove();
}
if (viewUpdatedCollection != null) {
viewUpdatedCollection.update(batchNewData, lastBatch);
}
// post
if (batchNewData != null || lastBatch != null) {
if (InstrumentationHelper.ENABLED) {
InstrumentationHelper.get().qViewIndicate(this, dataWindowViewFactory.getViewName(), batchNewData, lastBatch);
}
updateChildren(batchNewData, lastBatch);
if (InstrumentationHelper.ENABLED) {
InstrumentationHelper.get().aViewIndicate();
}
}
// clear
lastBatch = batchNewData;
if (aggregationService != null) {
aggregationService.applyLeave(batchNewData, null, agentInstanceContext);
}
oldestEvent = window.iterator().next();
}
}
public void visitView(ViewDataVisitor viewDataVisitor) {
viewDataVisitor.visitPrimary(window, true, dataWindowViewFactory.getViewName(), null);
viewDataVisitor.visitPrimary(lastBatch, dataWindowViewFactory.getViewName());
}
private boolean evaluateExpression(EventBean arriving, int windowSize) {
ExpressionViewOAFieldEnum.populate(builtinEventProps.getProperties(), windowSize, oldestEventTimestamp, newestEventTimestamp, this, 0, oldestEvent, newestEvent);
eventsPerStream[0] = arriving;
for (AggregationServiceAggExpressionDesc aggregateNode : aggregateNodes) {
aggregateNode.assignFuture(aggregationService);
}
Boolean result = (Boolean) expiryExpression.evaluate(eventsPerStream, true, agentInstanceContext);
if (result == null) {
return false;
}
return result;
}
public final Iterator<EventBean> iterator() {
return window.iterator();
}
// Handle variable updates by scheduling a re-evaluation with timers
public void update(Object newValue, Object oldValue) {
if (!agentInstanceContext.getStatementContext().getSchedulingService().isScheduled(scheduleHandle)) {
agentInstanceContext.getStatementContext().getSchedulingService().add(0, scheduleHandle, scheduleSlot);
}
}
public ViewFactory getViewFactory() {
return dataWindowViewFactory;
}
}