/*
* *************************************************************************************
* 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.core.context.util;
import com.espertech.esper.client.EventBean;
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.start.EPStatementStopMethodImpl;
import com.espertech.esper.epl.expression.*;
import com.espertech.esper.epl.script.AgentInstanceScriptContext;
import com.espertech.esper.epl.view.OutputProcessViewTerminable;
import com.espertech.esper.event.MappedEventBean;
import com.espertech.esper.filter.FilterHandle;
import com.espertech.esper.filter.FilterHandleCallback;
import com.espertech.esper.util.StopCallback;
import com.espertech.esper.view.Viewable;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.util.*;
public class StatementAgentInstanceUtil {
private static final Log log = LogFactory.getLog(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) {
if (agentInstances == null) {
return;
}
for (AgentInstance instance : agentInstances) {
stopAgentInstance(instance, terminationProperties, servicesContext, isStatementStop);
}
}
public static void stopAgentInstance(AgentInstance agentInstance, Map<String, Object> terminationProperties, EPServicesContext servicesContext, boolean isStatementStop) {
if (terminationProperties != null) {
agentInstance.getAgentInstanceContext().getContextProperties().getProperties().putAll(terminationProperties);
}
StatementAgentInstanceUtil.stop(agentInstance.getStopCallback(), agentInstance.getAgentInstanceContext(), agentInstance.getFinalView(), servicesContext, isStatementStop);
}
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) {
log.warn("Failed to perform statement stop for statement '" + statementContext.getStatementName() +
"' expression '" + statementContext.getExpression() + "' : " + e.getMessage(), e);
}
}
public static void stop(StopCallback stopCallback, AgentInstanceContext agentInstanceContext, Viewable finalView, EPServicesContext servicesContext, boolean isStatementStop) {
// obtain statement lock
StatementAgentInstanceLock lock = agentInstanceContext.getEpStatementAgentInstanceHandle().getStatementAgentInstanceLock();
lock.acquireWriteLock(null);
try {
if (finalView instanceof OutputProcessViewTerminable && !isStatementStop) {
OutputProcessViewTerminable terminable = (OutputProcessViewTerminable) finalView;
terminable.terminated();
}
stopSafe(stopCallback, agentInstanceContext.getStatementContext());
if (servicesContext.getSchedulableAgentInstanceDirectory() != null) {
servicesContext.getSchedulableAgentInstanceDirectory().remove(agentInstanceContext.getStatementContext().getStatementId(), agentInstanceContext.getAgentInstanceId());
}
// indicate method resolution
agentInstanceContext.getStatementContext().getMethodResolutionService().destroyedAgentInstance(agentInstanceContext.getAgentInstanceId());
// 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 (agentInstanceContext.getStatementContext().getExtensionServicesContext() != null) {
agentInstanceContext.getStatementContext().getExtensionServicesContext().endContextPartition(agentInstanceContext.getAgentInstanceId());
}
}
finally {
lock.releaseWriteLock(null);
}
}
public static StatementAgentInstanceFactoryResult start(EPServicesContext servicesContext,
ContextControllerStatementBase statement,
boolean isSingleInstanceContext,
int agentInstanceId,
MappedEventBean agentInstanceProperties,
AgentInstanceFilterProxy agentInstanceFilterProxy,
boolean isRecoveringResilient)
{
StatementContext statementContext = statement.getStatementContext();
// make a new lock for the agent instance or use the already-allocated default lock
StatementAgentInstanceLock agentInstanceLock;
if (isSingleInstanceContext) {
agentInstanceLock = statementContext.getDefaultAgentInstanceLock();
}
else {
agentInstanceLock = servicesContext.getStatementLockFactory().getStatementLock(statementContext.getStatementName(), statementContext.getExpression(), 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);
// create agent instance context
AgentInstanceScriptContext agentInstanceScriptContext = null;
if (statementContext.getDefaultAgentInstanceScriptContext() != null) {
agentInstanceScriptContext = new AgentInstanceScriptContext();
}
AgentInstanceContext agentInstanceContext = new AgentInstanceContext(statementContext, agentInstanceHandle, agentInstanceId, agentInstanceFilterProxy, agentInstanceProperties, agentInstanceScriptContext);
StatementAgentInstanceLock statementAgentInstanceLock = agentInstanceContext.getEpStatementAgentInstanceHandle().getStatementAgentInstanceLock();
statementAgentInstanceLock.acquireWriteLock(null);
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());
}
// execute preloads, if any
for (StatementAgentInstancePreload preload : startResult.getPreloadList()) {
preload.executePreload();
}
if (statementContext.getExtensionServicesContext() != null) {
statementContext.getExtensionServicesContext().startContextPartition(startResult, agentInstanceId);
}
// instantiate
return startResult;
}
finally {
statementAgentInstanceLock.releaseWriteLock(null);
}
}
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().equals(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
String statementId = filterHandle.getStatementId();
AgentInstance agentInstanceFound = null;
for (AgentInstance agentInstance : agentInstances) {
if (agentInstance.getAgentInstanceContext().getStatementId().equals(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;
q.add(handleCallback);
}
else {
ArrayDeque<EPStatementHandleCallback> q = new ArrayDeque<EPStatementHandleCallback>(4);
q.add((EPStatementHandleCallback) stmtCallback);
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 == filterHandle) {
return true;
}
}
agentInstanceContext.getEpStatementAgentInstanceHandle().internalDispatch(agentInstanceContext);
}
catch (RuntimeException ex) {
servicesContext.getExceptionHandlingService().handleException(ex, agentInstanceContext.getEpStatementAgentInstanceHandle());
}
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(servicesContext.getStatementLockFactory());
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(agentInstanceContext);
}
catch (RuntimeException ex) {
servicesContext.getExceptionHandlingService().handleException(ex, agentInstanceContext.getEpStatementAgentInstanceHandle());
}
finally {
agentInstanceContext.getAgentInstanceLock().releaseWriteLock(servicesContext.getStatementLockFactory());
}
}
}