/* *************************************************************************************** * 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.epl.core; import com.espertech.esper.client.ConfigurationInformation; import com.espertech.esper.client.EPException; import com.espertech.esper.client.EventBean; import com.espertech.esper.client.EventType; import com.espertech.esper.collection.IterablesArrayIterator; import com.espertech.esper.collection.Pair; import com.espertech.esper.core.service.StatementContext; import com.espertech.esper.epl.db.DataCache; import com.espertech.esper.epl.db.PollExecStrategy; import com.espertech.esper.epl.expression.core.*; import com.espertech.esper.epl.expression.visitor.ExprNodeIdentifierVisitor; import com.espertech.esper.epl.join.pollindex.PollResultIndexingStrategy; import com.espertech.esper.epl.join.table.EventTable; import com.espertech.esper.epl.join.table.UnindexedEventTableList; import com.espertech.esper.epl.spec.MethodStreamSpec; import com.espertech.esper.epl.table.mgmt.TableService; import com.espertech.esper.epl.variable.VariableReader; import com.espertech.esper.epl.variable.VariableService; import com.espertech.esper.event.EventAdapterService; import com.espertech.esper.schedule.SchedulingService; import com.espertech.esper.schedule.TimeProvider; import com.espertech.esper.util.JavaClassHelper; import com.espertech.esper.view.HistoricalEventViewable; import com.espertech.esper.view.View; import com.espertech.esper.view.ViewSupport; import net.sf.cglib.reflect.FastMethod; import java.util.*; /** * Polling-data provider that calls a static method on a class and passed parameters, and wraps the * results as POJO events. */ public class MethodPollingViewable implements HistoricalEventViewable { private final MethodStreamSpec methodStreamSpec; private final DataCache dataCache; private final EventType eventType; private final ThreadLocal<DataCache> dataCacheThreadLocal = new ThreadLocal<DataCache>(); private final ExprEvaluatorContext exprEvaluatorContext; private final MethodPollingViewableMeta metadata; private PollExecStrategy pollExecStrategy; private SortedSet<Integer> requiredStreams; private ExprEvaluator[] validatedExprNodes; private StatementContext statementContext; private static final EventBean[][] NULL_ROWS; static { NULL_ROWS = new EventBean[1][]; NULL_ROWS[0] = new EventBean[1]; } private static final PollResultIndexingStrategy ITERATOR_INDEXING_STRATEGY = new PollResultIndexingStrategy() { public EventTable[] index(List<EventBean> pollResult, boolean isActiveCache, StatementContext statementContext) { return new EventTable[]{new UnindexedEventTableList(pollResult, -1)}; } public String toQueryPlan() { return this.getClass().getSimpleName() + " unindexed"; } }; public MethodPollingViewable( MethodStreamSpec methodStreamSpec, DataCache dataCache, EventType eventType, ExprEvaluatorContext exprEvaluatorContext, MethodPollingViewableMeta metadata) { this.methodStreamSpec = methodStreamSpec; this.dataCache = dataCache; this.eventType = eventType; this.exprEvaluatorContext = exprEvaluatorContext; this.metadata = metadata; } public void stop() { pollExecStrategy.destroy(); dataCache.destroy(); } public ThreadLocal<DataCache> getDataCacheThreadLocal() { return dataCacheThreadLocal; } public DataCache getOptionalDataCache() { return dataCache; } public void validate(EngineImportService engineImportService, StreamTypeService streamTypeService, TimeProvider timeProvider, VariableService variableService, TableService tableService, ExprEvaluatorContext exprEvaluatorContext, ConfigurationInformation configSnapshot, SchedulingService schedulingService, String engineURI, Map<Integer, List<ExprNode>> sqlParameters, EventAdapterService eventAdapterService, StatementContext statementContext) throws ExprValidationException { this.statementContext = statementContext; // validate and visit ExprValidationContext validationContext = new ExprValidationContext(streamTypeService, engineImportService, statementContext.getStatementExtensionServicesContext(), null, timeProvider, variableService, tableService, exprEvaluatorContext, eventAdapterService, statementContext.getStatementName(), statementContext.getStatementId(), statementContext.getAnnotations(), null, false, false, true, false, null, false); ExprNodeIdentifierVisitor visitor = new ExprNodeIdentifierVisitor(true); final List<ExprNode> validatedInputParameters = new ArrayList<ExprNode>(); for (ExprNode exprNode : methodStreamSpec.getExpressions()) { ExprNode validated = ExprNodeUtility.getValidatedSubtree(ExprNodeOrigin.METHODINVJOIN, exprNode, validationContext); validatedInputParameters.add(validated); validated.accept(visitor); } // determine required streams requiredStreams = new TreeSet<Integer>(); for (Pair<Integer, String> identifier : visitor.getExprProperties()) { requiredStreams.add(identifier.getFirst()); } // class-based evaluation if (metadata.getMethodProviderClass() != null) { // resolve actual method to use ExprNodeUtilResolveExceptionHandler handler = new ExprNodeUtilResolveExceptionHandler() { public ExprValidationException handle(Exception e) { if (methodStreamSpec.getExpressions().size() == 0) { return new ExprValidationException("Method footprint does not match the number or type of expression parameters, expecting no parameters in method: " + e.getMessage()); } Class[] resultTypes = ExprNodeUtility.getExprResultTypes(validatedInputParameters); return new ExprValidationException("Method footprint does not match the number or type of expression parameters, expecting a method where parameters are typed '" + JavaClassHelper.getParameterAsString(resultTypes) + "': " + e.getMessage()); } }; ExprNodeUtilMethodDesc desc = ExprNodeUtility.resolveMethodAllowWildcardAndStream( metadata.getMethodProviderClass().getName(), metadata.isStaticMethod() ? null : metadata.getMethodProviderClass(), methodStreamSpec.getMethodName(), validatedInputParameters, engineImportService, eventAdapterService, statementContext.getStatementId(), false, null, handler, methodStreamSpec.getMethodName(), tableService, statementContext.getEngineURI()); validatedExprNodes = desc.getChildEvals(); // Construct polling strategy as a method invocation Object invocationTarget = metadata.getInvocationTarget(); MethodPollingExecStrategyEnum strategy = metadata.getStrategy(); VariableReader variableReader = metadata.getVariableReader(); String variableName = metadata.getVariableName(); FastMethod methodFastClass = desc.getFastMethod(); if (metadata.getEventTypeEventBeanArray() != null) { pollExecStrategy = new MethodPollingExecStrategyEventBeans(eventAdapterService, methodFastClass, eventType, invocationTarget, strategy, variableReader, variableName, variableService); } else if (metadata.getOptionalMapType() != null) { if (desc.getFastMethod().getReturnType().isArray()) { pollExecStrategy = new MethodPollingExecStrategyMapArray(eventAdapterService, methodFastClass, eventType, invocationTarget, strategy, variableReader, variableName, variableService); } else if (metadata.isCollection()) { pollExecStrategy = new MethodPollingExecStrategyMapCollection(eventAdapterService, methodFastClass, eventType, invocationTarget, strategy, variableReader, variableName, variableService); } else if (metadata.isIterator()) { pollExecStrategy = new MethodPollingExecStrategyMapIterator(eventAdapterService, methodFastClass, eventType, invocationTarget, strategy, variableReader, variableName, variableService); } else { pollExecStrategy = new MethodPollingExecStrategyMapPlain(eventAdapterService, methodFastClass, eventType, invocationTarget, strategy, variableReader, variableName, variableService); } } else if (metadata.getOptionalOaType() != null) { if (desc.getFastMethod().getReturnType() == Object[][].class) { pollExecStrategy = new MethodPollingExecStrategyOAArray(eventAdapterService, methodFastClass, eventType, invocationTarget, strategy, variableReader, variableName, variableService); } else if (metadata.isCollection()) { pollExecStrategy = new MethodPollingExecStrategyOACollection(eventAdapterService, methodFastClass, eventType, invocationTarget, strategy, variableReader, variableName, variableService); } else if (metadata.isIterator()) { pollExecStrategy = new MethodPollingExecStrategyOAIterator(eventAdapterService, methodFastClass, eventType, invocationTarget, strategy, variableReader, variableName, variableService); } else { pollExecStrategy = new MethodPollingExecStrategyOAPlain(eventAdapterService, methodFastClass, eventType, invocationTarget, strategy, variableReader, variableName, variableService); } } else { if (desc.getFastMethod().getReturnType().isArray()) { pollExecStrategy = new MethodPollingExecStrategyPOJOArray(eventAdapterService, methodFastClass, eventType, invocationTarget, strategy, variableReader, variableName, variableService); } else if (metadata.isCollection()) { pollExecStrategy = new MethodPollingExecStrategyPOJOCollection(eventAdapterService, methodFastClass, eventType, invocationTarget, strategy, variableReader, variableName, variableService); } else if (metadata.isIterator()) { pollExecStrategy = new MethodPollingExecStrategyPOJOIterator(eventAdapterService, methodFastClass, eventType, invocationTarget, strategy, variableReader, variableName, variableService); } else { pollExecStrategy = new MethodPollingExecStrategyPOJOPlain(eventAdapterService, methodFastClass, eventType, invocationTarget, strategy, variableReader, variableName, variableService); } } } else { // script-based evaluation pollExecStrategy = new MethodPollingExecStrategyScript(metadata.getScriptExpression(), metadata.getEventTypeEventBeanArray()); validatedExprNodes = ExprNodeUtility.getEvaluators(validatedInputParameters); } } public EventTable[][] poll(EventBean[][] lookupEventsPerStream, PollResultIndexingStrategy indexingStrategy, ExprEvaluatorContext exprEvaluatorContext) { DataCache localDataCache = dataCacheThreadLocal.get(); boolean strategyStarted = false; EventTable[][] resultPerInputRow = new EventTable[lookupEventsPerStream.length][]; // Get input parameters for each row for (int row = 0; row < lookupEventsPerStream.length; row++) { Object[] methodParams = new Object[validatedExprNodes.length]; // Build lookup keys for (int valueNum = 0; valueNum < validatedExprNodes.length; valueNum++) { Object parameterValue = validatedExprNodes[valueNum].evaluate(lookupEventsPerStream[row], true, exprEvaluatorContext); methodParams[valueNum] = parameterValue; } EventTable[] result = null; // try the threadlocal iteration cache, if set if (localDataCache != null) { result = localDataCache.getCached(methodParams, methodStreamSpec.getExpressions().size()); } // try the connection cache if (result == null) { result = dataCache.getCached(methodParams, methodStreamSpec.getExpressions().size()); if ((result != null) && (localDataCache != null)) { localDataCache.put(methodParams, methodStreamSpec.getExpressions().size(), result); } } if (result != null) { // found in cache resultPerInputRow[row] = result; } else { // not found in cache, get from actual polling (db query) try { if (!strategyStarted) { pollExecStrategy.start(); strategyStarted = true; } // Poll using the polling execution strategy and lookup values List<EventBean> pollResult = pollExecStrategy.poll(methodParams, exprEvaluatorContext); // index the result, if required, using an indexing strategy EventTable[] indexTable = indexingStrategy.index(pollResult, dataCache.isActive(), statementContext); // assign to row resultPerInputRow[row] = indexTable; // save in cache dataCache.put(methodParams, methodStreamSpec.getExpressions().size(), indexTable); if (localDataCache != null) { localDataCache.put(methodParams, methodStreamSpec.getExpressions().size(), indexTable); } } catch (EPException ex) { if (strategyStarted) { pollExecStrategy.done(); } throw ex; } } } if (strategyStarted) { pollExecStrategy.done(); } return resultPerInputRow; } public View addView(View view) { view.setParent(this); return view; } public View[] getViews() { return ViewSupport.EMPTY_VIEW_ARRAY; } public boolean removeView(View view) { throw new UnsupportedOperationException("Subviews not supported"); } public void removeAllViews() { throw new UnsupportedOperationException("Subviews not supported"); } public boolean hasViews() { return false; } public EventType getEventType() { return eventType; } public Iterator<EventBean> iterator() { EventTable[][] result = poll(NULL_ROWS, ITERATOR_INDEXING_STRATEGY, exprEvaluatorContext); return new IterablesArrayIterator(result); } public SortedSet<Integer> getRequiredStreams() { return requiredStreams; } public boolean hasRequiredStreams() { return !requiredStreams.isEmpty(); } }