com.espertech.esper.core.start.EPPreparedExecuteMethod.java Source code

Java tutorial

Introduction

Here is the source code for com.espertech.esper.core.start.EPPreparedExecuteMethod.java

Source

/**************************************************************************************
 * 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.start;

import com.espertech.esper.client.EventBean;
import com.espertech.esper.client.EventType;
import com.espertech.esper.client.context.ContextPartitionSelector;
import com.espertech.esper.client.context.ContextPartitionSelectorAll;
import com.espertech.esper.collection.MultiKey;
import com.espertech.esper.collection.Pair;
import com.espertech.esper.collection.UniformPair;
import com.espertech.esper.core.context.mgr.ContextManager;
import com.espertech.esper.core.context.mgr.ContextPropertyRegistryImpl;
import com.espertech.esper.core.context.util.AgentInstanceContext;
import com.espertech.esper.core.service.EPPreparedQueryResult;
import com.espertech.esper.core.service.EPServicesContext;
import com.espertech.esper.core.service.StatementContext;
import com.espertech.esper.core.service.StreamJoinAnalysisResult;
import com.espertech.esper.epl.core.*;
import com.espertech.esper.epl.expression.ExprEvaluator;
import com.espertech.esper.epl.expression.ExprNode;
import com.espertech.esper.epl.expression.ExprNodeUtility;
import com.espertech.esper.epl.expression.ExprValidationException;
import com.espertech.esper.epl.join.base.*;
import com.espertech.esper.epl.named.NamedWindowProcessor;
import com.espertech.esper.epl.named.NamedWindowProcessorInstance;
import com.espertech.esper.epl.spec.NamedWindowConsumerStreamSpec;
import com.espertech.esper.epl.spec.StatementSpecCompiled;
import com.espertech.esper.epl.spec.StreamSpecCompiled;
import com.espertech.esper.event.EventBeanReader;
import com.espertech.esper.event.EventBeanReaderDefaultImpl;
import com.espertech.esper.event.EventBeanUtility;
import com.espertech.esper.event.EventTypeSPI;
import com.espertech.esper.filter.FilterSpecCompiled;
import com.espertech.esper.filter.FilterSpecCompiler;
import com.espertech.esper.util.AuditPath;
import com.espertech.esper.view.Viewable;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.util.*;

/**
 * Starts and provides the stop method for EPL statements.
 */
public class EPPreparedExecuteMethod {
    private static final Log queryPlanLog = LogFactory.getLog(AuditPath.QUERYPLAN_LOG);
    private static final Log log = LogFactory.getLog(EPPreparedExecuteMethod.class);

    private final StatementSpecCompiled statementSpec;
    private final ResultSetProcessor resultSetProcessor;
    private final NamedWindowProcessor[] processors;
    private final AgentInstanceContext agentInstanceContext;
    private final EPServicesContext services;
    private EventBeanReader eventBeanReader;
    private JoinSetComposerPrototype joinSetComposerPrototype;
    private final FilterSpecCompiled[] filters;

    /**
     * Ctor.
     * @param statementSpec is a container for the definition of all statement constructs that
     * may have been used in the statement, i.e. if defines the select clauses, insert into, outer joins etc.
     * @param services is the service instances for dependency injection
     * @param statementContext is statement-level information and statement services
     * @throws ExprValidationException if the preparation failed
     */
    public EPPreparedExecuteMethod(StatementSpecCompiled statementSpec, EPServicesContext services,
            StatementContext statementContext) throws ExprValidationException {
        boolean queryPlanLogging = services.getConfigSnapshot().getEngineDefaults().getLogging()
                .isEnableQueryPlan();
        if (queryPlanLogging) {
            queryPlanLog.info("Query plans for Fire-and-forget query '" + statementContext.getExpression() + "'");
        }

        this.statementSpec = statementSpec;
        this.services = services;

        validateExecuteQuery();

        int numStreams = statementSpec.getStreamSpecs().size();
        EventType[] typesPerStream = new EventType[numStreams];
        String[] namesPerStream = new String[numStreams];
        processors = new NamedWindowProcessor[numStreams];
        agentInstanceContext = new AgentInstanceContext(statementContext, null, -1, null, null,
                statementContext.getDefaultAgentInstanceScriptContext());

        // resolve types and named window processors
        for (int i = 0; i < numStreams; i++) {
            final StreamSpecCompiled streamSpec = statementSpec.getStreamSpecs().get(i);
            NamedWindowConsumerStreamSpec namedSpec = (NamedWindowConsumerStreamSpec) streamSpec;

            String streamName = namedSpec.getWindowName();
            if (namedSpec.getOptionalStreamName() != null) {
                streamName = namedSpec.getOptionalStreamName();
            }
            namesPerStream[i] = streamName;

            processors[i] = services.getNamedWindowService().getProcessor(namedSpec.getWindowName());
            if (processors[i] == null) {
                throw new ExprValidationException(
                        "A named window by name '" + namedSpec.getWindowName() + "' does not exist");
            }
            typesPerStream[i] = processors[i].getTailView().getEventType();
        }

        // compile filter to optimize access to named window
        filters = new FilterSpecCompiled[numStreams];
        if (statementSpec.getFilterRootNode() != null) {
            LinkedHashMap<String, Pair<EventType, String>> tagged = new LinkedHashMap<String, Pair<EventType, String>>();
            for (int i = 0; i < numStreams; i++) {
                try {
                    StreamTypeServiceImpl types = new StreamTypeServiceImpl(typesPerStream, namesPerStream,
                            new boolean[numStreams], services.getEngineURI(), false);
                    filters[i] = FilterSpecCompiler.makeFilterSpec(typesPerStream[i], namesPerStream[i],
                            Collections.singletonList(statementSpec.getFilterRootNode()), null, tagged, tagged,
                            types, null, statementContext, Collections.singleton(i));
                } catch (Exception ex) {
                    log.warn("Unexpected exception analyzing filter paths: " + ex.getMessage(), ex);
                }
            }
        }

        // obtain result set processor
        boolean[] isIStreamOnly = new boolean[namesPerStream.length];
        Arrays.fill(isIStreamOnly, true);
        StreamTypeService typeService = new StreamTypeServiceImpl(typesPerStream, namesPerStream, isIStreamOnly,
                services.getEngineURI(), true);
        EPStatementStartMethodHelperValidate.validateNodes(statementSpec, statementContext, typeService, null);

        ResultSetProcessorFactoryDesc resultSetProcessorPrototype = ResultSetProcessorFactoryFactory
                .getProcessorPrototype(statementSpec, statementContext, typeService, null, new boolean[0], true,
                        ContextPropertyRegistryImpl.EMPTY_REGISTRY, null);
        resultSetProcessor = EPStatementStartMethodHelperAssignExpr
                .getAssignResultSetProcessor(agentInstanceContext, resultSetProcessorPrototype);

        if (statementSpec.getSelectClauseSpec().isDistinct()) {
            if (resultSetProcessor.getResultEventType() instanceof EventTypeSPI) {
                eventBeanReader = ((EventTypeSPI) resultSetProcessor.getResultEventType()).getReader();
            }
            if (eventBeanReader == null) {
                eventBeanReader = new EventBeanReaderDefaultImpl(resultSetProcessor.getResultEventType());
            }
        }

        // plan joins or simple queries
        if (numStreams > 1) {
            StreamJoinAnalysisResult streamJoinAnalysisResult = new StreamJoinAnalysisResult(numStreams);
            Arrays.fill(streamJoinAnalysisResult.getNamedWindow(), true);
            for (int i = 0; i < numStreams; i++) {
                NamedWindowProcessorInstance processorInstance = processors[i]
                        .getProcessorInstance(agentInstanceContext);
                if (processors[i].isVirtualDataWindow()) {
                    streamJoinAnalysisResult.getViewExternal()[i] = processorInstance.getRootViewInstance()
                            .getVirtualDataWindow();
                }
                String[][] uniqueIndexes = processors[i].getUniqueIndexes(processorInstance);
                streamJoinAnalysisResult.getUniqueKeys()[i] = uniqueIndexes;
            }

            boolean hasAggregations = !resultSetProcessorPrototype.getAggregationServiceFactoryDesc()
                    .getExpressions().isEmpty();
            joinSetComposerPrototype = JoinSetComposerPrototypeFactory.makeComposerPrototype(null, null,
                    statementSpec.getOuterJoinDescList(), statementSpec.getFilterRootNode(), typesPerStream,
                    namesPerStream, streamJoinAnalysisResult, queryPlanLogging, statementContext.getAnnotations(),
                    new HistoricalViewableDesc(numStreams), agentInstanceContext, false, hasAggregations);
        }

        // check context partition use
        if (statementSpec.getOptionalContextName() != null) {
            if (numStreams > 1) {
                throw new ExprValidationException(
                        "Joins in runtime queries for context partitions are not supported");
            }
        }
    }

    /**
     * Returns the event type of the prepared statement.
     * @return event type
     */
    public EventType getEventType() {
        return resultSetProcessor.getResultEventType();
    }

    /**
     * Executes the prepared query.
     * @return query results
     */
    public EPPreparedQueryResult execute(ContextPartitionSelector[] contextPartitionSelectors) {
        int numStreams = processors.length;

        if (contextPartitionSelectors != null && contextPartitionSelectors.length != numStreams) {
            throw new IllegalArgumentException(
                    "Number of context partition selectors does not match the number of named windows in the from-clause");
        }

        // handle non-context case
        if (statementSpec.getOptionalContextName() == null) {

            Collection<EventBean>[] snapshots = new Collection[numStreams];
            for (int i = 0; i < numStreams; i++) {

                ContextPartitionSelector selector = contextPartitionSelectors == null ? null
                        : contextPartitionSelectors[i];
                snapshots[i] = getStreamFilterSnapshot(i, selector);
            }

            resultSetProcessor.clear();
            return process(snapshots);
        }

        List<ContextPartitionResult> contextPartitionResults = new ArrayList<ContextPartitionResult>();

        // context partition runtime query
        Collection<Integer> agentInstanceIds;
        if (contextPartitionSelectors == null
                || contextPartitionSelectors[0] instanceof ContextPartitionSelectorAll) {
            agentInstanceIds = processors[0].getProcessorInstancesAll();
        } else {
            ContextManager contextManager = services.getContextManagementService()
                    .getContextManager(statementSpec.getOptionalContextName());
            agentInstanceIds = contextManager.getAgentInstanceIds(contextPartitionSelectors[0]);
        }

        // collect events and agent instances
        for (int agentInstanceId : agentInstanceIds) {
            NamedWindowProcessorInstance processorInstance = processors[0].getProcessorInstance(agentInstanceId);
            if (processorInstance != null) {
                Collection<EventBean> coll = processorInstance.getTailViewInstance().snapshot(filters[0],
                        statementSpec.getAnnotations());
                contextPartitionResults.add(new ContextPartitionResult(coll,
                        processorInstance.getTailViewInstance().getAgentInstanceContext()));
            }
        }

        // process context partitions
        ArrayDeque<EventBean[]> events = new ArrayDeque<EventBean[]>();
        for (ContextPartitionResult contextPartitionResult : contextPartitionResults) {
            Collection<EventBean> snapshot = contextPartitionResult.getEvents();
            if (statementSpec.getFilterRootNode() != null) {
                snapshot = getFiltered(snapshot, Collections.singletonList(statementSpec.getFilterRootNode()));
            }
            EventBean[] rows = snapshot.toArray(new EventBean[snapshot.size()]);
            resultSetProcessor.setAgentInstanceContext(contextPartitionResult.getContext());
            UniformPair<EventBean[]> results = resultSetProcessor.processViewResult(rows, null, true);
            if (results != null && results.getFirst() != null && results.getFirst().length > 0) {
                events.add(results.getFirst());
            }
        }
        return new EPPreparedQueryResult(resultSetProcessor.getResultEventType(), EventBeanUtility.flatten(events));
    }

    private Collection<EventBean> getStreamFilterSnapshot(int streamNum,
            ContextPartitionSelector contextPartitionSelector) {
        final StreamSpecCompiled streamSpec = statementSpec.getStreamSpecs().get(streamNum);
        NamedWindowConsumerStreamSpec namedSpec = (NamedWindowConsumerStreamSpec) streamSpec;
        NamedWindowProcessor namedWindowProcessor = processors[streamNum];

        // handle the case of a single or matching agent instance
        NamedWindowProcessorInstance processorInstance = namedWindowProcessor
                .getProcessorInstance(agentInstanceContext);
        if (processorInstance != null) {
            return getStreamSnapshotInstance(streamNum, namedSpec, processorInstance);
        }

        // context partition runtime query
        Collection<Integer> contextPartitions;
        if (contextPartitionSelector == null || contextPartitionSelector instanceof ContextPartitionSelectorAll) {
            contextPartitions = namedWindowProcessor.getProcessorInstancesAll();
        } else {
            ContextManager contextManager = services.getContextManagementService()
                    .getContextManager(namedWindowProcessor.getContextName());
            contextPartitions = contextManager.getAgentInstanceIds(contextPartitionSelector);
        }

        // collect events
        ArrayDeque<EventBean> events = new ArrayDeque<EventBean>();
        for (int agentInstanceId : contextPartitions) {
            processorInstance = namedWindowProcessor.getProcessorInstance(agentInstanceId);
            if (processorInstance != null) {
                Collection<EventBean> coll = processorInstance.getTailViewInstance().snapshot(filters[streamNum],
                        statementSpec.getAnnotations());
                events.addAll(coll);
            }
        }
        return events;
    }

    private Collection<EventBean> getStreamSnapshotInstance(int streamNum, NamedWindowConsumerStreamSpec namedSpec,
            NamedWindowProcessorInstance processorInstance) {
        Collection<EventBean> coll = processorInstance.getTailViewInstance().snapshot(filters[streamNum],
                statementSpec.getAnnotations());
        if (namedSpec.getFilterExpressions().size() != 0) {
            coll = getFiltered(coll, namedSpec.getFilterExpressions());
        }
        return coll;
    }

    private EPPreparedQueryResult process(Collection<EventBean>[] snapshots) {

        int numStreams = processors.length;

        UniformPair<EventBean[]> results;
        if (numStreams == 1) {
            if (statementSpec.getFilterRootNode() != null) {
                snapshots[0] = getFiltered(snapshots[0], Arrays.asList(statementSpec.getFilterRootNode()));
            }
            EventBean[] rows = snapshots[0].toArray(new EventBean[snapshots[0].size()]);
            results = resultSetProcessor.processViewResult(rows, null, true);
        } else {
            Viewable[] viewablePerStream = new Viewable[numStreams];
            for (int i = 0; i < numStreams; i++) {
                NamedWindowProcessorInstance instance = processors[i].getProcessorInstance(agentInstanceContext);
                if (instance == null) {
                    throw new UnsupportedOperationException(
                            "Joins against named windows that are under context are not supported");
                }
                viewablePerStream[i] = instance.getTailViewInstance();
            }

            JoinSetComposerDesc joinSetComposerDesc = joinSetComposerPrototype.create(viewablePerStream, true);
            JoinSetComposer joinComposer = joinSetComposerDesc.getJoinSetComposer();
            JoinSetFilter joinFilter;
            if (joinSetComposerDesc.getPostJoinFilterEvaluator() != null) {
                joinFilter = new JoinSetFilter(joinSetComposerDesc.getPostJoinFilterEvaluator());
            } else {
                joinFilter = null;
            }

            EventBean[][] oldDataPerStream = new EventBean[numStreams][];
            EventBean[][] newDataPerStream = new EventBean[numStreams][];
            for (int i = 0; i < numStreams; i++) {
                newDataPerStream[i] = snapshots[i].toArray(new EventBean[snapshots[i].size()]);
            }
            UniformPair<Set<MultiKey<EventBean>>> result = joinComposer.join(newDataPerStream, oldDataPerStream,
                    agentInstanceContext);
            if (joinFilter != null) {
                joinFilter.process(result.getFirst(), null, agentInstanceContext);
            }
            results = resultSetProcessor.processJoinResult(result.getFirst(), null, true);
        }

        if (statementSpec.getSelectClauseSpec().isDistinct()) {
            results.setFirst(EventBeanUtility.getDistinctByProp(results.getFirst(), eventBeanReader));
        }

        return new EPPreparedQueryResult(resultSetProcessor.getResultEventType(), results.getFirst());
    }

    private void validateExecuteQuery() throws ExprValidationException {
        if (statementSpec.getSubSelectExpressions().size() > 0) {
            throw new ExprValidationException("Subqueries are not a supported feature of on-demand queries");
        }
        for (int i = 0; i < statementSpec.getStreamSpecs().size(); i++) {
            if (!(statementSpec.getStreamSpecs().get(i) instanceof NamedWindowConsumerStreamSpec)) {
                throw new ExprValidationException(
                        "On-demand queries require named windows and do not allow event streams or patterns");
            }
            if (statementSpec.getStreamSpecs().get(i).getViewSpecs().size() != 0) {
                throw new ExprValidationException("Views are not a supported feature of on-demand queries");
            }
        }
        if (statementSpec.getOutputLimitSpec() != null) {
            throw new ExprValidationException(
                    "Output rate limiting is not a supported feature of on-demand queries");
        }
        if (statementSpec.getInsertIntoDesc() != null) {
            throw new ExprValidationException("Insert-into is not a supported feature of on-demand queries");
        }
    }

    private List<EventBean> getFiltered(Collection<EventBean> snapshot, List<ExprNode> filterExpressions) {
        EventBean[] eventsPerStream = new EventBean[1];
        List<EventBean> filteredSnapshot = new ArrayList<EventBean>();
        ExprEvaluator[] evaluators = ExprNodeUtility.getEvaluators(filterExpressions);
        for (EventBean row : snapshot) {
            boolean pass = true;
            eventsPerStream[0] = row;
            for (ExprEvaluator filter : evaluators) {
                Boolean result = (Boolean) filter.evaluate(eventsPerStream, true, agentInstanceContext);
                if (result == null || !result) {
                    pass = false;
                    break;
                }
            }

            if (pass) {
                filteredSnapshot.add(row);
            }
        }

        return filteredSnapshot;
    }

    public FilterSpecCompiled[] getFilters() {
        return filters;
    }

    public NamedWindowProcessor[] getProcessors() {
        return processors;
    }

    private static class ContextPartitionResult {
        private final Collection<EventBean> events;
        private final AgentInstanceContext context;

        private ContextPartitionResult(Collection<EventBean> events, AgentInstanceContext context) {
            this.events = events;
            this.context = context;
        }

        public Collection<EventBean> getEvents() {
            return events;
        }

        public AgentInstanceContext getContext() {
            return context;
        }
    }
}