/************************************************************************************** * 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.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.epl.db.DataCache; import com.espertech.esper.epl.db.PollExecStrategy; import com.espertech.esper.epl.expression.*; 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.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 java.lang.annotation.Annotation; 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 PollExecStrategy pollExecStrategy; private final List<ExprNode> inputParameters; private final DataCache dataCache; private final EventType eventType; private final ThreadLocal<DataCache> dataCacheThreadLocal = new ThreadLocal<DataCache>(); private final ExprEvaluatorContext exprEvaluatorContext; private SortedSet<Integer> requiredStreams; private ExprEvaluator[] validatedExprNodes; private static final EventBean[][] NULL_ROWS; static { NULL_ROWS = new EventBean[1][]; NULL_ROWS[0] = new EventBean[1]; } private static final PollResultIndexingStrategy iteratorIndexingStrategy = new PollResultIndexingStrategy() { public EventTable index(List<EventBean> pollResult, boolean isActiveCache) { return new UnindexedEventTableList(pollResult); } public String toQueryPlan() { return this.getClass().getSimpleName() + " unindexed"; } }; /** * Ctor. * @param methodStreamSpec defines class and method names * @param myStreamNumber is the stream number * @param inputParameters the input parameter expressions * @param pollExecStrategy the execution strategy * @param dataCache the cache to use * @param eventType the type of event returned * @param exprEvaluatorContext expression evaluation context */ public MethodPollingViewable( MethodStreamSpec methodStreamSpec, int myStreamNumber, List<ExprNode> inputParameters, PollExecStrategy pollExecStrategy, DataCache dataCache, EventType eventType, ExprEvaluatorContext exprEvaluatorContext) { this.methodStreamSpec = methodStreamSpec; this.inputParameters = inputParameters; this.pollExecStrategy = pollExecStrategy; this.dataCache = dataCache; this.eventType = eventType; this.exprEvaluatorContext = exprEvaluatorContext; } public void stop() { pollExecStrategy.destroy(); } public ThreadLocal<DataCache> getDataCacheThreadLocal() { return dataCacheThreadLocal; } public void validate(EngineImportService engineImportService, StreamTypeService streamTypeService, MethodResolutionService methodResolutionService, TimeProvider timeProvider, VariableService variableService, ExprEvaluatorContext exprEvaluatorContext, ConfigurationInformation configSnapshot, SchedulingService schedulingService, String engineURI, Map<Integer, List<ExprNode>> sqlParameters, EventAdapterService eventAdapterService, String statementName, String statementId, Annotation[] annotations) throws ExprValidationException { Class[] paramTypes = new Class[inputParameters.size()]; int count = 0; validatedExprNodes = new ExprEvaluator[inputParameters.size()]; requiredStreams = new TreeSet<Integer>(); ExprNodeIdentifierVisitor visitor = new ExprNodeIdentifierVisitor(true); ExprValidationContext validationContext = new ExprValidationContext(streamTypeService, methodResolutionService, null, timeProvider, variableService, exprEvaluatorContext, eventAdapterService, statementName, statementId, annotations, null); for (ExprNode exprNode : inputParameters) { ExprNode validated = ExprNodeUtility.getValidatedSubtree(exprNode, validationContext); ExprEvaluator evaluator = validated.getExprEvaluator(); validatedExprNodes[count] = evaluator; paramTypes[count] = evaluator.getType(); count++; validated.accept(visitor); } for (Pair<Integer, String> identifier : visitor.getExprProperties()) { requiredStreams.add(identifier.getFirst()); } // Try to resolve the method, also checking parameter types try { methodResolutionService.resolveMethod(methodStreamSpec.getClassName(), methodStreamSpec.getMethodName(), paramTypes); } catch(Exception e) { if (inputParameters.size() == 0) { throw new ExprValidationException("Method footprint does not match the number or type of expression parameters, expecting no parameters in method: " + e.getMessage()); } throw new ExprValidationException("Method footprint does not match the number or type of expression parameters, expecting a method where parameters are typed '" + JavaClassHelper.getParameterAsString(paramTypes) + "': " + e.getMessage()); } } 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[] lookupValues = new Object[inputParameters.size()]; // Build lookup keys for (int valueNum = 0; valueNum < inputParameters.size(); valueNum++) { Object parameterValue = validatedExprNodes[valueNum].evaluate(lookupEventsPerStream[row], true, exprEvaluatorContext); lookupValues[valueNum] = parameterValue; } EventTable result = null; // try the threadlocal iteration cache, if set if (localDataCache != null) { result = localDataCache.getCached(lookupValues); } // try the connection cache if (result == null) { result = dataCache.getCached(lookupValues); if ((result != null) && (localDataCache != null)) { localDataCache.put(lookupValues, 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(lookupValues); // index the result, if required, using an indexing strategy EventTable indexTable = indexingStrategy.index(pollResult, dataCache.isActive()); // assign to row resultPerInputRow[row] = indexTable; // save in cache dataCache.put(lookupValues, indexTable); if (localDataCache != null) { localDataCache.put(lookupValues, 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 List<View> getViews() { return Collections.emptyList(); } 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, iteratorIndexingStrategy, exprEvaluatorContext); return new IterablesArrayIterator(result); } public SortedSet<Integer> getRequiredStreams() { return requiredStreams; } public boolean hasRequiredStreams() { return !requiredStreams.isEmpty(); } }