/************************************************************************************** * 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.db; 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.epl.core.EngineImportService; import com.espertech.esper.epl.core.MethodResolutionService; import com.espertech.esper.epl.core.StreamTypeService; 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.variable.VariableService; import com.espertech.esper.event.EventAdapterService; import com.espertech.esper.schedule.SchedulingService; import com.espertech.esper.schedule.TimeProvider; import com.espertech.esper.view.HistoricalEventViewable; import com.espertech.esper.view.View; import java.lang.annotation.Annotation; import java.util.*; /** * Implements a poller viewable that uses a polling strategy, a cache and * some input parameters extracted from event streams to perform the polling. */ public class DatabasePollingViewable implements HistoricalEventViewable { private final int myStreamNumber; private final PollExecStrategy pollExecStrategy; private final List<String> inputParameters; private final DataCache dataCache; private final EventType eventType; private final ThreadLocal<DataCache> dataCacheThreadLocal = new ThreadLocal<DataCache>(); private ExprEvaluator[] evaluators; private SortedSet<Integer> subordinateStreams; private ExprEvaluatorContext exprEvaluatorContext; 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 myStreamNumber is the stream number of the view * @param inputParameters are the event property names providing input parameter keys * @param pollExecStrategy is the strategy to use for retrieving results * @param dataCache is looked up before using the strategy * @param eventType is the type of events generated by the view */ public DatabasePollingViewable(int myStreamNumber, List<String> inputParameters, PollExecStrategy pollExecStrategy, DataCache dataCache, EventType eventType) { this.myStreamNumber = myStreamNumber; this.inputParameters = inputParameters; this.pollExecStrategy = pollExecStrategy; this.dataCache = dataCache; this.eventType = eventType; } public void stop() { pollExecStrategy.destroy(); } 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 { evaluators = new ExprEvaluator[inputParameters.size()]; subordinateStreams = new TreeSet<Integer>(); this.exprEvaluatorContext = exprEvaluatorContext; int count = 0; ExprValidationContext validationContext = new ExprValidationContext(streamTypeService, methodResolutionService, null, timeProvider, variableService, exprEvaluatorContext, eventAdapterService, statementName, statementId, annotations, null); for (String inputParam : inputParameters) { ExprNode raw = findSQLExpressionNode(myStreamNumber, count, sqlParameters); if (raw == null) { throw new ExprValidationException("Internal error find expression for historical stream parameter " + count + " stream " + myStreamNumber); } ExprNode evaluator = ExprNodeUtility.getValidatedSubtree(raw, validationContext); evaluators[count++] = evaluator.getExprEvaluator(); ExprNodeIdentifierCollectVisitor visitor = new ExprNodeIdentifierCollectVisitor(); visitor.visit(evaluator); for (ExprIdentNode identNode : visitor.getExprProperties()) { if (identNode.getStreamId() == myStreamNumber) { throw new ExprValidationException("Invalid expression '" + inputParam + "' resolves to the historical data itself"); } subordinateStreams.add(identNode.getStreamId()); } } } 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 EventBean[] eventsPerStream; 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++) { eventsPerStream = lookupEventsPerStream[row]; Object lookupValue = evaluators[valueNum].evaluate(eventsPerStream, true, exprEvaluatorContext); lookupValues[valueNum] = lookupValue; } 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); } } // use the result from cache 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 boolean hasViews() { return false; } public EventType getEventType() { return eventType; } public Iterator<EventBean> iterator() { return new IterablesArrayIterator(poll(NULL_ROWS, iteratorIndexingStrategy, exprEvaluatorContext)); } public SortedSet<Integer> getRequiredStreams() { return subordinateStreams; } public boolean hasRequiredStreams() { return !subordinateStreams.isEmpty(); } public ThreadLocal<DataCache> getDataCacheThreadLocal() { return dataCacheThreadLocal; } public void removeAllViews() { throw new UnsupportedOperationException("Subviews not supported"); } private static ExprNode findSQLExpressionNode(int myStreamNumber, int count, Map<Integer, List<ExprNode>> sqlParameters) { if ((sqlParameters == null) || (sqlParameters.isEmpty())) { return null; } List<ExprNode> parameters = sqlParameters.get(myStreamNumber); if ((parameters == null) || (parameters.isEmpty()) || (parameters.size() < (count + 1))) { return null; } return parameters.get(count); } }