/*
***************************************************************************************
* 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.core.context.util;
import com.espertech.esper.client.EventBean;
import com.espertech.esper.client.hook.ExceptionHandlerExceptionType;
import com.espertech.esper.core.context.factory.StatementAgentInstanceFactoryResult;
import com.espertech.esper.core.context.factory.StatementAgentInstancePreload;
import com.espertech.esper.core.context.mgr.AgentInstance;
import com.espertech.esper.core.context.mgr.AgentInstanceFilterProxy;
import com.espertech.esper.core.context.mgr.ContextControllerStatementBase;
import com.espertech.esper.core.context.mgr.ContextControllerTreeAgentInstanceList;
import com.espertech.esper.core.context.stmt.AIRegistryAggregation;
import com.espertech.esper.core.context.stmt.AIRegistryExpr;
import com.espertech.esper.core.context.subselect.SubSelectStrategyHolder;
import com.espertech.esper.core.service.*;
import com.espertech.esper.core.service.resource.StatementResourceHolder;
import com.espertech.esper.core.start.EPStatementStopMethodImpl;
import com.espertech.esper.epl.expression.prev.ExprPreviousEvalStrategy;
import com.espertech.esper.epl.expression.prev.ExprPreviousNode;
import com.espertech.esper.epl.expression.prior.ExprPriorEvalStrategy;
import com.espertech.esper.epl.expression.prior.ExprPriorNode;
import com.espertech.esper.epl.expression.subquery.ExprSubselectNode;
import com.espertech.esper.epl.expression.table.ExprTableAccessEvalStrategy;
import com.espertech.esper.epl.expression.table.ExprTableAccessNode;
import com.espertech.esper.epl.named.NamedWindowProcessor;
import com.espertech.esper.epl.named.NamedWindowProcessorInstance;
import com.espertech.esper.epl.script.AgentInstanceScriptContext;
import com.espertech.esper.epl.spec.OnTriggerDesc;
import com.espertech.esper.epl.spec.OnTriggerWindowDesc;
import com.espertech.esper.epl.view.OutputProcessViewTerminable;
import com.espertech.esper.event.MappedEventBean;
import com.espertech.esper.filter.FilterHandle;
import com.espertech.esper.metrics.instrumentation.InstrumentationHelper;
import com.espertech.esper.rowregex.RegexExprPreviousEvalStrategy;
import com.espertech.esper.util.StopCallback;
import com.espertech.esper.view.Viewable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
public class StatementAgentInstanceUtil {
private static final Logger log = LoggerFactory.getLogger(EPStatementStopMethodImpl.class);
public static void handleFilterFault(EventBean theEvent, long version, EPServicesContext servicesContext, Map<Integer, ContextControllerTreeAgentInstanceList> agentInstanceListMap) {
for (Map.Entry<Integer, ContextControllerTreeAgentInstanceList> agentInstanceEntry : agentInstanceListMap.entrySet()) {
if (agentInstanceEntry.getValue().getFilterVersionAfterAllocation() > version) {
StatementAgentInstanceUtil.evaluateEventForStatement(servicesContext, theEvent, null, agentInstanceEntry.getValue().getAgentInstances());
}
}
}
public static void stopAgentInstances(List<AgentInstance> agentInstances, Map<String, Object> terminationProperties, EPServicesContext servicesContext, boolean isStatementStop, boolean leaveLocksAcquired) {
if (agentInstances == null) {
return;
}
for (AgentInstance instance : agentInstances) {
stopAgentInstanceRemoveResources(instance, terminationProperties, servicesContext, isStatementStop, leaveLocksAcquired);
}
}
public static void stopAgentInstanceRemoveResources(AgentInstance agentInstance, Map<String, Object> terminationProperties, EPServicesContext servicesContext, boolean isStatementStop, boolean leaveLocksAcquired) {
if (terminationProperties != null) {
agentInstance.getAgentInstanceContext().getContextProperties().getProperties().putAll(terminationProperties);
}
StatementAgentInstanceUtil.stop(agentInstance.getStopCallback(), agentInstance.getAgentInstanceContext(), agentInstance.getFinalView(), servicesContext, isStatementStop, leaveLocksAcquired, true);
}
public static void stopSafe(Collection<StopCallback> terminationCallbacks, StopCallback[] stopCallbacks, StatementContext statementContext) {
StopCallback[] terminationArr = terminationCallbacks.toArray(new StopCallback[terminationCallbacks.size()]);
stopSafe(terminationArr, statementContext);
stopSafe(stopCallbacks, statementContext);
}
public static void stopSafe(StopCallback[] stopMethods, StatementContext statementContext) {
for (StopCallback stopCallback : stopMethods) {
stopSafe(stopCallback, statementContext);
}
}
public static void stopSafe(StopCallback stopMethod, StatementContext statementContext) {
try {
stopMethod.stop();
} catch (RuntimeException e) {
statementContext.getExceptionHandlingService().handleException(e, statementContext.getStatementName(), statementContext.getExpression(), ExceptionHandlerExceptionType.STOP, null);
}
}
public static void stop(StopCallback stopCallback, AgentInstanceContext agentInstanceContext, Viewable finalView, EPServicesContext servicesContext, boolean isStatementStop, boolean leaveLocksAcquired, boolean removedStatementResources) {
if (InstrumentationHelper.ENABLED) {
InstrumentationHelper.get().qContextPartitionDestroy(agentInstanceContext);
}
// obtain statement lock
StatementAgentInstanceLock lock = agentInstanceContext.getEpStatementAgentInstanceHandle().getStatementAgentInstanceLock();
lock.acquireWriteLock();
try {
if (finalView instanceof OutputProcessViewTerminable && !isStatementStop) {
OutputProcessViewTerminable terminable = (OutputProcessViewTerminable) finalView;
terminable.terminated();
}
stopSafe(stopCallback, agentInstanceContext.getStatementContext());
// release resource
agentInstanceContext.getStatementContext().getStatementAgentInstanceRegistry().deassign(agentInstanceContext.getAgentInstanceId());
// cause any remaining schedules, that may concide with the caller's schedule, to be ignored
agentInstanceContext.getEpStatementAgentInstanceHandle().setDestroyed(true);
// cause any filters, that may concide with the caller's filters, to be ignored
agentInstanceContext.getEpStatementAgentInstanceHandle().getStatementFilterVersion().setStmtFilterVersion(Long.MAX_VALUE);
if (removedStatementResources &&
agentInstanceContext.getStatementContext().getStatementExtensionServicesContext() != null &&
agentInstanceContext.getStatementContext().getStatementExtensionServicesContext().getStmtResources() != null) {
agentInstanceContext.getStatementContext().getStatementExtensionServicesContext().getStmtResources().deallocatePartitioned(agentInstanceContext.getAgentInstanceId());
}
} finally {
if (!leaveLocksAcquired) {
if (agentInstanceContext.getStatementContext().getEpStatementHandle().isHasTableAccess()) {
agentInstanceContext.getTableExprEvaluatorContext().releaseAcquiredLocks();
}
lock.releaseWriteLock();
}
if (InstrumentationHelper.ENABLED) {
InstrumentationHelper.get().aContextPartitionDestroy();
}
}
}
public static StatementAgentInstanceFactoryResult start(EPServicesContext servicesContext,
ContextControllerStatementBase statement,
boolean isSingleInstanceContext,
int agentInstanceId,
MappedEventBean agentInstanceProperties,
AgentInstanceFilterProxy agentInstanceFilterProxy,
boolean isRecoveringResilient) {
StatementContext statementContext = statement.getStatementContext();
// for on-trigger statements against named windows we must use the named window lock
OnTriggerDesc optOnTriggerDesc = statement.getStatementSpec().getOnTriggerDesc();
String namedWindowName = null;
if ((optOnTriggerDesc != null) && (optOnTriggerDesc instanceof OnTriggerWindowDesc)) {
String windowName = ((OnTriggerWindowDesc) optOnTriggerDesc).getWindowName();
if (servicesContext.getTableService().getTableMetadata(windowName) == null) {
namedWindowName = windowName;
}
}
// determine lock to use
StatementAgentInstanceLock agentInstanceLock;
if (namedWindowName != null) {
NamedWindowProcessor processor = servicesContext.getNamedWindowMgmtService().getProcessor(namedWindowName);
NamedWindowProcessorInstance instance = processor.getProcessorInstance(agentInstanceId);
agentInstanceLock = instance.getRootViewInstance().getAgentInstanceContext().getEpStatementAgentInstanceHandle().getStatementAgentInstanceLock();
} else {
if (isSingleInstanceContext) {
agentInstanceLock = statementContext.getDefaultAgentInstanceLock();
} else {
agentInstanceLock = servicesContext.getStatementLockFactory().getStatementLock(statementContext.getStatementName(), statementContext.getAnnotations(), statementContext.isStatelessSelect());
}
}
// share the filter version between agent instance handle (callbacks) and agent instance context
StatementAgentInstanceFilterVersion filterVersion = new StatementAgentInstanceFilterVersion();
// create handle that comtains lock for use in scheduling and filter callbacks
EPStatementAgentInstanceHandle agentInstanceHandle = new EPStatementAgentInstanceHandle(statementContext.getEpStatementHandle(), agentInstanceLock, agentInstanceId, filterVersion, statementContext.getFilterFaultHandlerFactory());
// create agent instance context
AgentInstanceScriptContext agentInstanceScriptContext = null;
if (statementContext.getDefaultAgentInstanceScriptContext() != null) {
agentInstanceScriptContext = AgentInstanceScriptContext.from(statementContext.getEventAdapterService());
}
AgentInstanceContext agentInstanceContext = new AgentInstanceContext(statementContext, agentInstanceHandle, agentInstanceId, agentInstanceFilterProxy, agentInstanceProperties, agentInstanceScriptContext);
StatementAgentInstanceLock statementAgentInstanceLock = agentInstanceContext.getEpStatementAgentInstanceHandle().getStatementAgentInstanceLock();
if (InstrumentationHelper.ENABLED) {
InstrumentationHelper.get().qContextPartitionAllocate(agentInstanceContext);
}
statementAgentInstanceLock.acquireWriteLock();
try {
// start
StatementAgentInstanceFactoryResult startResult = statement.getFactory().newContext(agentInstanceContext, isRecoveringResilient);
// hook up with listeners+subscribers
startResult.getFinalView().addView(statement.getMergeView()); // hook output to merge view
// assign agents for expression-node based strategies
AIRegistryExpr aiExprSvc = statementContext.getStatementAgentInstanceRegistry().getAgentInstanceExprService();
AIRegistryAggregation aiAggregationSvc = statementContext.getStatementAgentInstanceRegistry().getAgentInstanceAggregationService();
// allocate aggregation service
if (startResult.getOptionalAggegationService() != null) {
aiAggregationSvc.assignService(agentInstanceId, startResult.getOptionalAggegationService());
}
// allocate subquery
for (Map.Entry<ExprSubselectNode, SubSelectStrategyHolder> item : startResult.getSubselectStrategies().entrySet()) {
ExprSubselectNode node = item.getKey();
SubSelectStrategyHolder strategyHolder = item.getValue();
aiExprSvc.getSubselectService(node).assignService(agentInstanceId, strategyHolder.getStategy());
aiExprSvc.getSubselectAggregationService(node).assignService(agentInstanceId, strategyHolder.getSubselectAggregationService());
// allocate prior within subquery
for (Map.Entry<ExprPriorNode, ExprPriorEvalStrategy> priorEntry : strategyHolder.getPriorStrategies().entrySet()) {
aiExprSvc.getPriorServices(priorEntry.getKey()).assignService(agentInstanceId, priorEntry.getValue());
}
// allocate previous within subquery
for (Map.Entry<ExprPreviousNode, ExprPreviousEvalStrategy> prevEntry : strategyHolder.getPreviousNodeStrategies().entrySet()) {
aiExprSvc.getPreviousServices(prevEntry.getKey()).assignService(agentInstanceId, prevEntry.getValue());
}
}
// allocate prior-expressions
for (Map.Entry<ExprPriorNode, ExprPriorEvalStrategy> item : startResult.getPriorNodeStrategies().entrySet()) {
aiExprSvc.getPriorServices(item.getKey()).assignService(agentInstanceId, item.getValue());
}
// allocate previous-expressions
for (Map.Entry<ExprPreviousNode, ExprPreviousEvalStrategy> item : startResult.getPreviousNodeStrategies().entrySet()) {
aiExprSvc.getPreviousServices(item.getKey()).assignService(agentInstanceId, item.getValue());
}
// allocate match-recognize previous expressions
RegexExprPreviousEvalStrategy regexExprPreviousEvalStrategy = startResult.getRegexExprPreviousEvalStrategy();
aiExprSvc.getMatchRecognizePrevious().assignService(agentInstanceId, regexExprPreviousEvalStrategy);
// allocate table-access-expressions
for (Map.Entry<ExprTableAccessNode, ExprTableAccessEvalStrategy> item : startResult.getTableAccessEvalStrategies().entrySet()) {
aiExprSvc.getTableAccessServices(item.getKey()).assignService(agentInstanceId, item.getValue());
}
// execute preloads, if any
for (StatementAgentInstancePreload preload : startResult.getPreloadList()) {
preload.executePreload(agentInstanceContext);
}
if (statementContext.getStatementExtensionServicesContext() != null && statementContext.getStatementExtensionServicesContext().getStmtResources() != null) {
StatementResourceHolder holder = statementContext.getStatementExtensionServicesContext().extractStatementResourceHolder(startResult);
statementContext.getStatementExtensionServicesContext().getStmtResources().setPartitioned(agentInstanceId, holder);
}
// instantiate
return startResult;
} finally {
if (agentInstanceContext.getStatementContext().getEpStatementHandle().isHasTableAccess()) {
agentInstanceContext.getTableExprEvaluatorContext().releaseAcquiredLocks();
}
statementAgentInstanceLock.releaseWriteLock();
if (InstrumentationHelper.ENABLED) {
InstrumentationHelper.get().aContextPartitionAllocate();
}
}
}
public static void evaluateEventForStatement(EPServicesContext servicesContext, EventBean theEvent, Map<String, Object> optionalTriggeringPattern, List<AgentInstance> agentInstances) {
if (theEvent != null) {
evaluateEventForStatementInternal(servicesContext, theEvent, agentInstances);
}
if (optionalTriggeringPattern != null) {
// evaluation order definition is up to the originator of the triggering pattern
for (Map.Entry<String, Object> entry : optionalTriggeringPattern.entrySet()) {
if (entry.getValue() instanceof EventBean) {
evaluateEventForStatementInternal(servicesContext, (EventBean) entry.getValue(), agentInstances);
} else if (entry.getValue() instanceof EventBean[]) {
EventBean[] eventsArray = (EventBean[]) entry.getValue();
for (EventBean eventElement : eventsArray) {
evaluateEventForStatementInternal(servicesContext, eventElement, agentInstances);
}
}
}
}
}
private static void evaluateEventForStatementInternal(EPServicesContext servicesContext, EventBean theEvent, List<AgentInstance> agentInstances) {
// context was created - reevaluate for the given event
ArrayDeque<FilterHandle> callbacks = new ArrayDeque<FilterHandle>(2);
servicesContext.getFilterService().evaluate(theEvent, callbacks); // evaluates for ALL statements
if (callbacks.isEmpty()) {
return;
}
// there is a single callback and a single context, if they match we are done
if (agentInstances.size() == 1 && callbacks.size() == 1) {
AgentInstance agentInstance = agentInstances.get(0);
if (agentInstance.getAgentInstanceContext().getStatementId() == callbacks.getFirst().getStatementId()) {
process(agentInstance, servicesContext, callbacks, theEvent);
}
return;
}
// use the right sorted/unsorted Map keyed by AgentInstance to sort
boolean isPrioritized = servicesContext.getConfigSnapshot().getEngineDefaults().getExecution().isPrioritized();
Map<AgentInstance, Object> stmtCallbacks;
if (!isPrioritized) {
stmtCallbacks = new HashMap<AgentInstance, Object>();
} else {
stmtCallbacks = new TreeMap<AgentInstance, Object>(AgentInstanceComparator.INSTANCE);
}
// process all callbacks
for (FilterHandle filterHandle : callbacks) {
// determine if this filter entry applies to any of the affected agent instances
int statementId = filterHandle.getStatementId();
AgentInstance agentInstanceFound = null;
for (AgentInstance agentInstance : agentInstances) {
if (agentInstance.getAgentInstanceContext().getStatementId() == statementId) {
agentInstanceFound = agentInstance;
break;
}
}
if (agentInstanceFound == null) { // when the callback is for some other stmt
continue;
}
EPStatementHandleCallback handleCallback = (EPStatementHandleCallback) filterHandle;
EPStatementAgentInstanceHandle handle = handleCallback.getAgentInstanceHandle();
// Self-joins require that the internal dispatch happens after all streams are evaluated.
// Priority or preemptive settings also require special ordering.
if (handle.isCanSelfJoin() || isPrioritized) {
Object stmtCallback = stmtCallbacks.get(agentInstanceFound);
if (stmtCallback == null) {
stmtCallbacks.put(agentInstanceFound, handleCallback);
} else if (stmtCallback instanceof ArrayDeque) {
ArrayDeque<EPStatementHandleCallback> q = (ArrayDeque<EPStatementHandleCallback>) stmtCallback;
if (!q.contains(handleCallback)) { // De-duplicate for Filter OR expression paths
q.add(handleCallback);
}
} else {
ArrayDeque<EPStatementHandleCallback> q = new ArrayDeque<EPStatementHandleCallback>(4);
q.add((EPStatementHandleCallback) stmtCallback);
if (stmtCallback != handleCallback) { // De-duplicate for Filter OR expression paths
q.add(handleCallback);
}
stmtCallbacks.put(agentInstanceFound, q);
}
continue;
}
// no need to be sorted, process
process(agentInstanceFound, servicesContext, Collections.<FilterHandle>singletonList(handleCallback), theEvent);
}
if (stmtCallbacks.isEmpty()) {
return;
}
// Process self-join or sorted prioritized callbacks
for (Map.Entry<AgentInstance, Object> entry : stmtCallbacks.entrySet()) {
AgentInstance agentInstance = entry.getKey();
Object callbackList = entry.getValue();
if (callbackList instanceof ArrayDeque) {
process(agentInstance, servicesContext, (Collection<FilterHandle>) callbackList, theEvent);
} else {
process(agentInstance, servicesContext, Collections.<FilterHandle>singletonList((FilterHandle) callbackList), theEvent);
}
if (agentInstance.getAgentInstanceContext().getEpStatementAgentInstanceHandle().isPreemptive()) {
return;
}
}
}
public static boolean evaluateFilterForStatement(EPServicesContext servicesContext, EventBean theEvent, AgentInstanceContext agentInstanceContext, FilterHandle filterHandle) {
// context was created - reevaluate for the given event
ArrayDeque<FilterHandle> callbacks = new ArrayDeque<FilterHandle>();
servicesContext.getFilterService().evaluate(theEvent, callbacks, agentInstanceContext.getStatementContext().getStatementId());
try {
servicesContext.getVariableService().setLocalVersion();
// sub-selects always go first
for (FilterHandle handle : callbacks) {
if (handle.equals(filterHandle)) {
return true;
}
}
agentInstanceContext.getEpStatementAgentInstanceHandle().internalDispatch();
} catch (RuntimeException ex) {
servicesContext.getExceptionHandlingService().handleException(ex, agentInstanceContext.getEpStatementAgentInstanceHandle(), ExceptionHandlerExceptionType.PROCESS, theEvent);
}
return false;
}
public static StopCallback getStopCallback(List<StopCallback> stopCallbacks, final AgentInstanceContext agentInstanceContext) {
final StopCallback[] stopCallbackArr = stopCallbacks.toArray(new StopCallback[stopCallbacks.size()]);
return new StopCallback() {
public void stop() {
StatementAgentInstanceUtil.stopSafe(agentInstanceContext.getTerminationCallbackRO(), stopCallbackArr, agentInstanceContext.getStatementContext());
}
};
}
private static void process(AgentInstance agentInstance,
EPServicesContext servicesContext,
Collection<FilterHandle> callbacks,
EventBean theEvent) {
AgentInstanceContext agentInstanceContext = agentInstance.getAgentInstanceContext();
agentInstance.getAgentInstanceContext().getAgentInstanceLock().acquireWriteLock();
try {
servicesContext.getVariableService().setLocalVersion();
// sub-selects always go first
for (FilterHandle handle : callbacks) {
EPStatementHandleCallback callback = (EPStatementHandleCallback) handle;
if (callback.getAgentInstanceHandle() != agentInstanceContext.getEpStatementAgentInstanceHandle()) {
continue;
}
callback.getFilterCallback().matchFound(theEvent, null);
}
agentInstanceContext.getEpStatementAgentInstanceHandle().internalDispatch();
} catch (RuntimeException ex) {
servicesContext.getExceptionHandlingService().handleException(ex, agentInstanceContext.getEpStatementAgentInstanceHandle(), ExceptionHandlerExceptionType.PROCESS, theEvent);
} finally {
if (agentInstanceContext.getStatementContext().getEpStatementHandle().isHasTableAccess()) {
agentInstanceContext.getTableExprEvaluatorContext().releaseAcquiredLocks();
}
agentInstanceContext.getAgentInstanceLock().releaseWriteLock();
}
}
}