/* *************************************************************************************** * 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.mgr; import com.espertech.esper.client.EventBean; import com.espertech.esper.client.EventType; import com.espertech.esper.client.SafeIterator; import com.espertech.esper.client.context.*; import com.espertech.esper.core.context.factory.StatementAgentInstanceFactoryResult; import com.espertech.esper.core.context.stmt.StatementAIResourceRegistryFactory; import com.espertech.esper.core.context.util.ContextDescriptor; import com.espertech.esper.core.context.util.ContextIteratorHandler; import com.espertech.esper.core.context.util.StatementAgentInstanceUtil; import com.espertech.esper.core.service.EPServicesContext; import com.espertech.esper.epl.expression.core.ExprValidationException; import com.espertech.esper.event.MappedEventBean; import com.espertech.esper.filter.FilterFaultHandler; import com.espertech.esper.filter.FilterSpecCompiled; import com.espertech.esper.filter.FilterSpecLookupable; import com.espertech.esper.filter.FilterValueSetParam; import java.util.*; public class ContextManagerImpl implements ContextManager, ContextControllerLifecycleCallback, ContextIteratorHandler, FilterFaultHandler { private final String contextName; private final EPServicesContext servicesContext; private final ContextControllerFactory factory; private final Map<Integer, ContextControllerStatementDesc> statements = new LinkedHashMap<Integer, ContextControllerStatementDesc>(); // retain order of statement creation private final ContextDescriptor contextDescriptor; private final Map<Integer, ContextControllerTreeAgentInstanceList> agentInstances = new LinkedHashMap<Integer, ContextControllerTreeAgentInstanceList>(); /** * The single root context. * This represents the context declared first. */ private ContextController rootContext; private final ContextPartitionIdManager contextPartitionIdManager; public ContextManagerImpl(ContextControllerFactoryServiceContext factoryServiceContext) throws ExprValidationException { this.contextName = factoryServiceContext.getContextName(); this.servicesContext = factoryServiceContext.getServicesContext(); this.factory = factoryServiceContext.getAgentInstanceContextCreate().getStatementContext().getContextControllerFactoryService().getFactory(factoryServiceContext)[0]; this.rootContext = factory.createNoCallback(0, this); // single instance: created here and activated/deactivated later this.contextPartitionIdManager = factoryServiceContext.getAgentInstanceContextCreate().getStatementContext().getContextControllerFactoryService().allocatePartitionIdMgr(contextName, factoryServiceContext.getAgentInstanceContextCreate().getStatementContext().getStatementId()); StatementAIResourceRegistryFactory resourceRegistryFactory = factory.getStatementAIResourceRegistryFactory(); Map<String, Object> contextProps = factory.getContextBuiltinProps(); EventType contextPropsType = servicesContext.getEventAdapterService().createAnonymousMapType(contextName, contextProps, true); ContextPropertyRegistryImpl registry = new ContextPropertyRegistryImpl(factory.getContextDetailPartitionItems(), contextPropsType); contextDescriptor = new ContextDescriptor(contextName, factory.isSingleInstanceContext(), registry, resourceRegistryFactory, this, factory.getContextDetail()); } public int getNumNestingLevels() { return 1; } public Map<Integer, ContextControllerStatementDesc> getStatements() { return statements; } public ContextDescriptor getContextDescriptor() { return contextDescriptor; } public ContextStateCache getContextStateCache() { return factory.getStateCache(); } public void addStatement(ContextControllerStatementBase statement, boolean isRecoveringResilient) throws ExprValidationException { // validation down the hierarchy ContextControllerStatementCtxCache caches = factory.validateStatement(statement); // add statement ContextControllerStatementDesc desc = new ContextControllerStatementDesc(statement, new ContextControllerStatementCtxCache[]{caches}); statements.put(statement.getStatementContext().getStatementId(), desc); // activate if this is the first statement if (statements.size() == 1) { activate(); // this may itself trigger a callback } else { // activate statement in respect to existing context partitions for (Map.Entry<Integer, ContextControllerTreeAgentInstanceList> entry : agentInstances.entrySet()) { if (entry.getValue().getState() == ContextPartitionState.STARTED) { AgentInstance agentInstance = startStatement(entry.getKey(), desc, rootContext, entry.getValue().getInitPartitionKey(), entry.getValue().getInitContextProperties(), isRecoveringResilient); entry.getValue().getAgentInstances().add(agentInstance); } } } } public synchronized void stopStatement(String statementName, int statementId) { destroyStatement(statementName, statementId); } public synchronized void destroyStatement(String statementName, int statementId) { if (!statements.containsKey(statementId)) { return; } if (statements.size() == 1) { safeDestroy(); } else { removeStatement(statementId); } } public void safeDestroy() { if (rootContext != null) { // deactivate rootContext.deactivate(); factory.getFactoryContext().getStateCache().removeContext(contextName); for (Map.Entry<Integer, ContextControllerTreeAgentInstanceList> entryCP : agentInstances.entrySet()) { StatementAgentInstanceUtil.stopAgentInstances(entryCP.getValue().getAgentInstances(), null, servicesContext, true, false); } agentInstances.clear(); contextPartitionIdManager.clear(); statements.clear(); } } public synchronized ContextControllerInstanceHandle contextPartitionInstantiate( Integer optionalContextPartitionId, int subPathId, Integer importSubpathId, ContextController originator, EventBean optionalTriggeringEvent, Map<String, Object> optionalTriggeringPattern, Object partitionKey, Map<String, Object> contextProperties, ContextControllerState states, ContextInternalFilterAddendum filterAddendum, boolean isRecoveringResilient, ContextPartitionState state) { // assign context id int assignedContextId; if (optionalContextPartitionId != null && !states.isImported()) { assignedContextId = optionalContextPartitionId; contextPartitionIdManager.addExisting(optionalContextPartitionId); } else { assignedContextId = contextPartitionIdManager.allocateId(); if (states != null && states.getPartitionImportCallback() != null && optionalContextPartitionId != null) { states.getPartitionImportCallback().allocated(assignedContextId, optionalContextPartitionId); } } // handle leaf creation List<AgentInstance> newInstances = new ArrayList<AgentInstance>(); if (state == ContextPartitionState.STARTED) { for (Map.Entry<Integer, ContextControllerStatementDesc> statementEntry : statements.entrySet()) { ContextControllerStatementDesc statementDesc = statementEntry.getValue(); AgentInstance instance = startStatement(assignedContextId, statementDesc, originator, partitionKey, contextProperties, isRecoveringResilient); newInstances.add(instance); } } // for all new contexts: evaluate this event for this statement if (optionalTriggeringEvent != null || optionalTriggeringPattern != null) { StatementAgentInstanceUtil.evaluateEventForStatement(servicesContext, optionalTriggeringEvent, optionalTriggeringPattern, newInstances); } // save leaf long filterVersion = servicesContext.getFilterService().getFiltersVersion(); ContextControllerTreeAgentInstanceList agentInstanceList = new ContextControllerTreeAgentInstanceList(filterVersion, partitionKey, contextProperties, newInstances, state); agentInstances.put(assignedContextId, agentInstanceList); return new ContextNestedHandleImpl(subPathId, assignedContextId, agentInstanceList); } public synchronized void contextPartitionTerminate(ContextControllerInstanceHandle contextNestedHandle, Map<String, Object> terminationProperties, boolean leaveLocksAcquired, List<AgentInstance> agentInstancesCollected) { ContextNestedHandleImpl handle = (ContextNestedHandleImpl) contextNestedHandle; ContextControllerTreeAgentInstanceList entry = agentInstances.remove(handle.getContextPartitionOrPathId()); if (entry != null) { StatementAgentInstanceUtil.stopAgentInstances(entry.getAgentInstances(), terminationProperties, servicesContext, false, leaveLocksAcquired); if (agentInstancesCollected != null) { agentInstancesCollected.addAll(entry.getAgentInstances()); } entry.getAgentInstances().clear(); contextPartitionIdManager.removeId(contextNestedHandle.getContextPartitionOrPathId()); } } public void contextPartitionNavigate(ContextControllerInstanceHandle existingHandle, ContextController originator, ContextControllerState controllerState, int exportedCPOrPathId, ContextInternalFilterAddendum filterAddendum, AgentInstanceSelector agentInstanceSelector, byte[] payload, boolean isRecoveringResilient) { ContextControllerTreeAgentInstanceList entry = agentInstances.get(existingHandle.getContextPartitionOrPathId()); if (entry == null) { return; } if (entry.getState() == ContextPartitionState.STOPPED) { entry.setState(ContextPartitionState.STARTED); entry.getAgentInstances().clear(); for (Map.Entry<Integer, ContextControllerStatementDesc> statement : statements.entrySet()) { AgentInstance instance = startStatement(existingHandle.getContextPartitionOrPathId(), statement.getValue(), originator, entry.getInitPartitionKey(), entry.getInitContextProperties(), false); entry.getAgentInstances().add(instance); } ContextStatePathKey key = new ContextStatePathKey(1, 0, existingHandle.getSubPathId()); ContextStatePathValue value = new ContextStatePathValue(existingHandle.getContextPartitionOrPathId(), payload, ContextPartitionState.STARTED); rootContext.getFactory().getFactoryContext().getStateCache().updateContextPath(contextName, key, value); } else { List<AgentInstance> removed = new ArrayList<AgentInstance>(2); List<AgentInstance> added = new ArrayList<AgentInstance>(2); for (AgentInstance agentInstance : entry.getAgentInstances()) { if (!agentInstanceSelector.select(agentInstance)) { continue; } // remove StatementAgentInstanceUtil.stopAgentInstanceRemoveResources(agentInstance, null, servicesContext, false, false); removed.add(agentInstance); // start ContextControllerStatementDesc statementDesc = statements.get(agentInstance.getAgentInstanceContext().getStatementId()); AgentInstance instance = startStatement(existingHandle.getContextPartitionOrPathId(), statementDesc, originator, entry.getInitPartitionKey(), entry.getInitContextProperties(), isRecoveringResilient); added.add(instance); if (controllerState.getPartitionImportCallback() != null) { controllerState.getPartitionImportCallback().existing(existingHandle.getContextPartitionOrPathId(), exportedCPOrPathId); } } entry.getAgentInstances().removeAll(removed); entry.getAgentInstances().addAll(added); } } public FilterSpecLookupable getFilterLookupable(EventType eventType) { return factory.getFilterLookupable(eventType); } public synchronized Iterator<EventBean> iterator(int statementId) { AgentInstance[] instances = getAgentInstancesForStmt(statementId); return new AgentInstanceArrayIterator(instances); } public synchronized SafeIterator<EventBean> safeIterator(int statementId) { AgentInstance[] instances = getAgentInstancesForStmt(statementId); return new AgentInstanceArraySafeIterator(instances); } public synchronized Iterator<EventBean> iterator(int statementId, ContextPartitionSelector selector) { AgentInstance[] instances = getAgentInstancesForStmt(statementId, selector); return new AgentInstanceArrayIterator(instances); } public synchronized SafeIterator<EventBean> safeIterator(int statementId, ContextPartitionSelector selector) { AgentInstance[] instances = getAgentInstancesForStmt(statementId, selector); return new AgentInstanceArraySafeIterator(instances); } public Collection<Integer> getAgentInstanceIds(ContextPartitionSelector selector) { if (selector instanceof ContextPartitionSelectorById) { ContextPartitionSelectorById byId = (ContextPartitionSelectorById) selector; Set<Integer> ids = byId.getContextPartitionIds(); if (ids == null || ids.isEmpty()) { return Collections.emptyList(); } ArrayList agentInstanceIds = new ArrayList<Integer>(ids); agentInstanceIds.retainAll(agentInstances.keySet()); return agentInstanceIds; } else if (selector instanceof ContextPartitionSelectorAll) { return new ArrayList<Integer>(agentInstances.keySet()); } else { ContextPartitionVisitorAgentInstanceId visitor = new ContextPartitionVisitorAgentInstanceId(1); rootContext.visitSelectedPartitions(selector, visitor); return visitor.getAgentInstanceIds(); } } public ContextStatePathDescriptor extractPaths(ContextPartitionSelector selector) { ContextPartitionVisitorState visitor = new ContextPartitionVisitorState(); rootContext.visitSelectedPartitions(selector, visitor); return new ContextStatePathDescriptor(visitor.getStates(), visitor.getContextPartitionInfo()); } public ContextStatePathDescriptor extractStopPaths(ContextPartitionSelector selector) { ContextStatePathDescriptor states = extractPaths(selector); for (Map.Entry<ContextStatePathKey, ContextStatePathValue> entry : states.getPaths().entrySet()) { int agentInstanceId = entry.getValue().getOptionalContextPartitionId(); ContextControllerTreeAgentInstanceList list = agentInstances.get(agentInstanceId); list.setState(ContextPartitionState.STOPPED); StatementAgentInstanceUtil.stopAgentInstances(list.getAgentInstances(), null, servicesContext, false, false); list.clearAgentInstances(); entry.getValue().setState(ContextPartitionState.STOPPED); rootContext.getFactory().getFactoryContext().getStateCache().updateContextPath(contextName, entry.getKey(), entry.getValue()); } return states; } public ContextStatePathDescriptor extractDestroyPaths(ContextPartitionSelector selector) { ContextStatePathDescriptor states = extractPaths(selector); for (Map.Entry<ContextStatePathKey, ContextStatePathValue> entry : states.getPaths().entrySet()) { int agentInstanceId = entry.getValue().getOptionalContextPartitionId(); ContextPartitionDescriptor descriptor = states.getContextPartitionInformation().get(agentInstanceId); rootContext.deletePath(descriptor.getIdentifier()); ContextControllerTreeAgentInstanceList list = agentInstances.remove(agentInstanceId); StatementAgentInstanceUtil.stopAgentInstances(list.getAgentInstances(), null, servicesContext, false, false); list.clearAgentInstances(); rootContext.getFactory().getFactoryContext().getStateCache().removeContextPath(contextName, entry.getKey().getLevel(), entry.getKey().getParentPath(), entry.getKey().getSubPath()); } return states; } public Map<Integer, ContextPartitionDescriptor> startPaths(ContextPartitionSelector selector) { ContextStatePathDescriptor states = extractPaths(selector); for (Map.Entry<ContextStatePathKey, ContextStatePathValue> entry : states.getPaths().entrySet()) { int agentInstanceId = entry.getValue().getOptionalContextPartitionId(); ContextControllerTreeAgentInstanceList list = agentInstances.get(agentInstanceId); if (list.getState() == ContextPartitionState.STARTED) { continue; } list.setState(ContextPartitionState.STARTED); entry.getValue().setState(ContextPartitionState.STARTED); for (Map.Entry<Integer, ContextControllerStatementDesc> statement : statements.entrySet()) { AgentInstance instance = startStatement(agentInstanceId, statement.getValue(), rootContext, list.getInitPartitionKey(), list.getInitContextProperties(), false); list.getAgentInstances().add(instance); } rootContext.getFactory().getFactoryContext().getStateCache().updateContextPath(contextName, entry.getKey(), entry.getValue()); } setState(states.getContextPartitionInformation(), ContextPartitionState.STARTED); return states.getContextPartitionInformation(); } public void importStartPaths(ContextControllerState state, AgentInstanceSelector agentInstanceSelector) { rootContext.importContextPartitions(state, 0, null, agentInstanceSelector); } public synchronized boolean handleFilterFault(EventBean theEvent, long version) { StatementAgentInstanceUtil.handleFilterFault(theEvent, version, servicesContext, agentInstances); return false; } private void activate() { rootContext.activate(null, null, null, null, null); } private AgentInstance[] getAgentInstancesForStmt(int statementId, ContextPartitionSelector selector) { Collection<Integer> agentInstanceIds = getAgentInstanceIds(selector); if (agentInstanceIds == null || agentInstanceIds.isEmpty()) { return new AgentInstance[0]; } List<AgentInstance> instances = new ArrayList<AgentInstance>(agentInstanceIds.size()); for (Integer agentInstanceId : agentInstanceIds) { ContextControllerTreeAgentInstanceList instancesList = agentInstances.get(agentInstanceId); if (instancesList != null) { Iterator<AgentInstance> instanceIt = instancesList.getAgentInstances().iterator(); for (; instanceIt.hasNext(); ) { AgentInstance instance = instanceIt.next(); if (instance.getAgentInstanceContext().getStatementContext().getStatementId() == statementId) { instances.add(instance); } } } } return instances.toArray(new AgentInstance[instances.size()]); } private AgentInstance[] getAgentInstancesForStmt(int statementId) { List<AgentInstance> instances = new ArrayList<AgentInstance>(); for (Map.Entry<Integer, ContextControllerTreeAgentInstanceList> contextPartitionEntry : agentInstances.entrySet()) { Iterator<AgentInstance> instanceIt = contextPartitionEntry.getValue().getAgentInstances().iterator(); for (; instanceIt.hasNext(); ) { AgentInstance instance = instanceIt.next(); if (instance.getAgentInstanceContext().getStatementContext().getStatementId() == statementId) { instances.add(instance); } } } return instances.toArray(new AgentInstance[instances.size()]); } private void removeStatement(int statementId) { ContextControllerStatementDesc statementDesc = statements.get(statementId); if (statementDesc == null) { return; } for (Map.Entry<Integer, ContextControllerTreeAgentInstanceList> contextPartitionEntry : agentInstances.entrySet()) { Iterator<AgentInstance> instanceIt = contextPartitionEntry.getValue().getAgentInstances().iterator(); for (; instanceIt.hasNext(); ) { AgentInstance instance = instanceIt.next(); if (instance.getAgentInstanceContext().getStatementContext().getStatementId() != statementId) { continue; } StatementAgentInstanceUtil.stop(instance.getStopCallback(), instance.getAgentInstanceContext(), instance.getFinalView(), servicesContext, true, false, true); instanceIt.remove(); } } statements.remove(statementId); } private AgentInstance startStatement(int contextId, ContextControllerStatementDesc statementDesc, ContextController originator, Object partitionKey, Map<String, Object> contextProperties, boolean isRecoveringResilient) { // build filters IdentityHashMap<FilterSpecCompiled, FilterValueSetParam[][]> filterAddendum = new IdentityHashMap<FilterSpecCompiled, FilterValueSetParam[][]>(); originator.getFactory().populateFilterAddendums(filterAddendum, statementDesc, partitionKey, contextId); AgentInstanceFilterProxy proxy = new AgentInstanceFilterProxyImpl(filterAddendum); // build built-in context properties contextProperties.put(ContextPropertyEventType.PROP_CTX_NAME, contextName); contextProperties.put(ContextPropertyEventType.PROP_CTX_ID, contextId); MappedEventBean contextBean = (MappedEventBean) servicesContext.getEventAdapterService().adapterForTypedMap(contextProperties, contextDescriptor.getContextPropertyRegistry().getContextEventType()); // activate StatementAgentInstanceFactoryResult result = StatementAgentInstanceUtil.start(servicesContext, statementDesc.getStatement(), false, contextId, contextBean, proxy, isRecoveringResilient); // save only instance data return new AgentInstance(result.getStopCallback(), result.getAgentInstanceContext(), result.getFinalView()); } protected static void setState(Map<Integer, ContextPartitionDescriptor> original, ContextPartitionState state) { for (Map.Entry<Integer, ContextPartitionDescriptor> entry : original.entrySet()) { entry.getValue().setState(state); } } public static class ContextNestedHandleImpl implements ContextControllerInstanceHandle { private final int subPathId; private final int contextPartitionId; private final ContextControllerTreeAgentInstanceList instances; public ContextNestedHandleImpl(int subPathId, int contextPartitionId, ContextControllerTreeAgentInstanceList instances) { this.subPathId = subPathId; this.contextPartitionId = contextPartitionId; this.instances = instances; } public Integer getContextPartitionOrPathId() { return contextPartitionId; } public ContextControllerTreeAgentInstanceList getInstances() { return instances; } public int getSubPathId() { return subPathId; } } }