/* * ************************************************************************************* * 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.context.mgr; import com.espertech.esper.client.EventPropertyGetter; import com.espertech.esper.client.EventType; import com.espertech.esper.collection.Pair; import com.espertech.esper.core.context.stmt.*; import com.espertech.esper.epl.core.EngineImportSingleRowDesc; import com.espertech.esper.epl.expression.ExprEvaluator; import com.espertech.esper.epl.expression.ExprNode; import com.espertech.esper.epl.expression.ExprValidationException; import com.espertech.esper.epl.spec.ContextDetail; import com.espertech.esper.epl.spec.ContextDetailHash; import com.espertech.esper.epl.spec.ContextDetailHashItem; import com.espertech.esper.epl.spec.ContextDetailPartitionItem; import com.espertech.esper.epl.spec.util.StatementSpecCompiledAnalyzer; import com.espertech.esper.epl.spec.util.StatementSpecCompiledAnalyzerResult; import com.espertech.esper.event.EventTypeUtility; import com.espertech.esper.filter.*; import java.io.StringWriter; import java.util.*; public class ContextControllerHashFactory extends ContextControllerFactoryBase implements ContextControllerFactory { private final ContextDetailHash hashedSpec; private final List<FilterSpecCompiled> filtersSpecsNestedContexts; private final ContextStateCache stateCache; private final ContextStatePathValueBinding binding; private Map<String, Object> contextBuiltinProps; public ContextControllerHashFactory(ContextControllerFactoryContext factoryContext, ContextDetailHash hashedSpec, List<FilterSpecCompiled> filtersSpecsNestedContexts, ContextStateCache stateCache) { super(factoryContext); this.hashedSpec = hashedSpec; this.filtersSpecsNestedContexts = filtersSpecsNestedContexts; this.stateCache = stateCache; this.binding = stateCache.getBinding(Integer.class); } public boolean hasFiltersSpecsNestedContexts() { return filtersSpecsNestedContexts != null && !filtersSpecsNestedContexts.isEmpty(); } public ContextStateCache getStateCache() { return stateCache; } public ContextStatePathValueBinding getBinding() { return binding; } public void validateFactory() throws ExprValidationException { validatePopulateContextDesc(); contextBuiltinProps = ContextPropertyEventType.getHashType(); } public ContextControllerStatementCtxCache validateStatement(ContextControllerStatementBase statement) throws ExprValidationException { StatementSpecCompiledAnalyzerResult streamAnalysis = StatementSpecCompiledAnalyzer.analyzeFilters(statement.getStatementSpec()); ContextControllerPartitionedUtil.validateStatementForContext(factoryContext.getContextName(), statement, streamAnalysis, getItemEventTypes(hashedSpec), factoryContext.getServicesContext().getNamedWindowService()); return new ContextControllerStatementCtxCacheFilters(streamAnalysis.getFilters()); } public void populateFilterAddendums(IdentityHashMap<FilterSpecCompiled, List<FilterValueSetParam>> filterAddendum, ContextControllerStatementDesc statement, Object key, int contextId) { ContextControllerStatementCtxCacheFilters statementInfo = (ContextControllerStatementCtxCacheFilters) statement.getCaches()[factoryContext.getNestingLevel() - 1]; int assignedContextPartition = (Integer) key; int code = assignedContextPartition % hashedSpec.getGranularity(); getAddendumFilters(filterAddendum, code, statementInfo.getFilterSpecs(), hashedSpec, statement); } public void populateContextInternalFilterAddendums(ContextInternalFilterAddendum filterAddendum, Object key) { int assignedContextPartition = (Integer) key; int code = assignedContextPartition % hashedSpec.getGranularity(); getAddendumFilters(filterAddendum.getFilterAddendum(), code, filtersSpecsNestedContexts, hashedSpec, null); } public FilterSpecLookupable getFilterLookupable(EventType eventType) { for (ContextDetailHashItem hashItem : hashedSpec.getItems()) { if (hashItem.getFilterSpecCompiled().getFilterForEventType() == eventType) { return hashItem.getLookupable(); } } return null; } public boolean isSingleInstanceContext() { return false; } public StatementAIResourceRegistryFactory getStatementAIResourceRegistryFactory() { if (hashedSpec.getGranularity() <= 65536) { return new StatementAIResourceRegistryFactory() { public StatementAIResourceRegistry make() { return new StatementAIResourceRegistry(new AIRegistryAggregationMultiPerm(), new AIRegistryExprMultiPerm()); } }; } else { return new StatementAIResourceRegistryFactory() { public StatementAIResourceRegistry make() { return new StatementAIResourceRegistry(new AIRegistryAggregationMap(), new AIRegistryExprMap()); } }; } } public List<ContextDetailPartitionItem> getContextDetailPartitionItems() { return Collections.emptyList(); } public ContextDetail getContextDetail() { return hashedSpec; } public ContextDetailHash getHashedSpec() { return hashedSpec; } public Map<String, Object> getContextBuiltinProps() { return contextBuiltinProps; } public ContextController createNoCallback(int pathId, ContextControllerLifecycleCallback callback) { return new ContextControllerHash(pathId, callback, this); } private Collection<EventType> getItemEventTypes(ContextDetailHash hashedSpec) { List<EventType> itemEventTypes = new ArrayList<EventType>(); for (ContextDetailHashItem item : hashedSpec.getItems()) { itemEventTypes.add(item.getFilterSpecCompiled().getFilterForEventType()); } return itemEventTypes; } private void validatePopulateContextDesc() throws ExprValidationException { if (hashedSpec.getItems().isEmpty()) { throw new ExprValidationException("Empty list of hash items"); } for (ContextDetailHashItem item : hashedSpec.getItems()) { if (item.getFunction().getParameters().isEmpty()) { throw new ExprValidationException("For context '" + factoryContext.getContextName() + "' expected one or more parameters to the hash function, but found no parameter list"); } // determine type of hash to use String hashFuncName = item.getFunction().getName(); HashFunctionEnum hashFunction = HashFunctionEnum.determine(factoryContext.getContextName(), hashFuncName); Pair<Class, EngineImportSingleRowDesc> hashSingleRowFunction = null; if (hashFunction == null) { try { hashSingleRowFunction = factoryContext.getAgentInstanceContextCreate().getStatementContext().getMethodResolutionService().resolveSingleRow(hashFuncName); } catch (Exception e) { // expected } if (hashSingleRowFunction == null) { throw new ExprValidationException("For context '" + factoryContext.getContextName() + "' expected a hash function that is any of {" + HashFunctionEnum.getStringList() + "} or a plug-in single-row function or script but received '" + hashFuncName + "'"); } } // get first parameter ExprNode paramExpr = item.getFunction().getParameters().get(0); ExprEvaluator eval = paramExpr.getExprEvaluator(); Class paramType = eval.getType(); EventPropertyGetter getter; if (hashFunction == HashFunctionEnum.CONSISTENT_HASH_CRC32) { if (item.getFunction().getParameters().size() > 1 || paramType != String.class) { getter = new ContextControllerHashedGetterCRC32Serialized(factoryContext.getAgentInstanceContextCreate().getStatementContext().getStatementName(), item.getFunction().getParameters(), hashedSpec.getGranularity()); } else { getter = new ContextControllerHashedGetterCRC32Single(eval, hashedSpec.getGranularity()); } } else if (hashFunction == HashFunctionEnum.HASH_CODE) { if (item.getFunction().getParameters().size() > 1) { getter = new ContextControllerHashedGetterHashMultiple(item.getFunction().getParameters(), hashedSpec.getGranularity()); } else { getter = new ContextControllerHashedGetterHashSingle(eval, hashedSpec.getGranularity()); } } else if (hashSingleRowFunction != null) { getter = new ContextControllerHashedGetterSingleRow(factoryContext.getAgentInstanceContextCreate().getStatementContext().getStatementName(), hashFuncName, hashSingleRowFunction, item.getFunction().getParameters(), hashedSpec.getGranularity(), factoryContext.getAgentInstanceContextCreate().getStatementContext().getMethodResolutionService(), item.getFilterSpecCompiled().getFilterForEventType()); } else { throw new IllegalArgumentException("Unrecognized hash code function '" + hashFuncName + "'"); } String expression = item.getFunction().getName() + "(" + paramExpr + ")"; FilterSpecLookupable lookupable = new FilterSpecLookupable(expression, getter, Integer.class); item.setLookupable(lookupable); } } // Compare filters in statement with filters in segmented context, addendum filter compilation private static void getAddendumFilters(IdentityHashMap<FilterSpecCompiled, List<FilterValueSetParam>> addendums, int agentInstanceId, List<FilterSpecCompiled> filtersSpecs, ContextDetailHash hashSpec, ContextControllerStatementDesc statementDesc) { // determine whether create-named-window boolean isCreateWindow = statementDesc != null && statementDesc.getStatement().getStatementSpec().getCreateWindowDesc() != null; if (!isCreateWindow) { for (FilterSpecCompiled filtersSpec : filtersSpecs) { ContextDetailHashItem foundPartition = findHashItemSpec(hashSpec, filtersSpec); if (foundPartition == null) { continue; } List<FilterValueSetParam> addendumFilters = new ArrayList<FilterValueSetParam>(); addendumFilters.addAll(foundPartition.getParametersCompiled()); FilterValueSetParam filter = new FilterValueSetParamImpl(foundPartition.getLookupable(), FilterOperator.EQUAL, agentInstanceId); addendumFilters.add(filter); List<FilterValueSetParam> existing = addendums.get(filtersSpec); if (existing == null) { addendums.put(filtersSpec, addendumFilters); } else { existing.addAll(addendumFilters); } } } // handle segmented context for create-window else { String declaredAsName = statementDesc.getStatement().getStatementSpec().getCreateWindowDesc().getAsEventTypeName(); if (declaredAsName != null) { for (FilterSpecCompiled filterSpec : filtersSpecs) { ContextDetailHashItem foundPartition = null; for (ContextDetailHashItem partitionItem : hashSpec.getItems()) { if (partitionItem.getFilterSpecCompiled().getFilterForEventType().getName().equals(declaredAsName)) { foundPartition = partitionItem; break; } } if (foundPartition == null) { continue; } FilterValueSetParam filter = new FilterValueSetParamImpl(foundPartition.getLookupable(), FilterOperator.EQUAL, agentInstanceId); List<FilterValueSetParam> existing = addendums.get(filterSpec); if (existing == null) { addendums.put(filterSpec, Collections.singletonList(filter)); } else { existing.add(filter); } } } } } public static ContextDetailHashItem findHashItemSpec(ContextDetailHash hashSpec, FilterSpecCompiled filterSpec) { ContextDetailHashItem foundPartition = null; for (ContextDetailHashItem partitionItem : hashSpec.getItems()) { boolean typeOrSubtype = EventTypeUtility.isTypeOrSubTypeOf(filterSpec.getFilterForEventType(), partitionItem.getFilterSpecCompiled().getFilterForEventType()); if (typeOrSubtype) { foundPartition = partitionItem; } } return foundPartition; } public static enum HashFunctionEnum { CONSISTENT_HASH_CRC32, HASH_CODE; private static String stringList; public static HashFunctionEnum determine(String contextName, String name) throws ExprValidationException { String nameTrim = name.toLowerCase().trim(); for (HashFunctionEnum val : HashFunctionEnum.values()) { if (val.name().toLowerCase().trim().equals(nameTrim)) { return val; } } return null; } public static String getStringList() { StringWriter message = new StringWriter(); String delimiter = ""; for (HashFunctionEnum val : HashFunctionEnum.values()) { message.append(delimiter); message.append(val.name().toLowerCase().trim()); delimiter = ", "; } return message.toString(); } } }