/* *************************************************************************************** * 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.core.context.mgr; import com.espertech.esper.client.EventPropertyGetter; import com.espertech.esper.client.EventType; import com.espertech.esper.client.context.ContextPartitionIdentifier; import com.espertech.esper.client.context.ContextPartitionIdentifierHash; 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.core.ExprEvaluator; import com.espertech.esper.epl.expression.core.ExprNode; import com.espertech.esper.epl.expression.core.ExprNodeUtility; import com.espertech.esper.epl.expression.core.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 abstract class ContextControllerHashFactoryBase extends ContextControllerFactoryBase implements ContextControllerFactory { private final ContextDetailHash hashedSpec; private final List<FilterSpecCompiled> filtersSpecsNestedContexts; private Map<String, Object> contextBuiltinProps; private Map<EventType, FilterSpecLookupable> nonPropertyExpressions = new HashMap<EventType, FilterSpecLookupable>(); public ContextControllerHashFactoryBase(ContextControllerFactoryContext factoryContext, ContextDetailHash hashedSpec, List<FilterSpecCompiled> filtersSpecsNestedContexts) { super(factoryContext); this.hashedSpec = hashedSpec; this.filtersSpecsNestedContexts = filtersSpecsNestedContexts; } public boolean hasFiltersSpecsNestedContexts() { return filtersSpecsNestedContexts != null && !filtersSpecsNestedContexts.isEmpty(); } 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().getNamedWindowMgmtService()); // register non-property expression to be able to recreated indexes for (Map.Entry<EventType, FilterSpecLookupable> entry : nonPropertyExpressions.entrySet()) { factoryContext.getServicesContext().getFilterNonPropertyRegisteryService().registerNonPropertyExpression(statement.getStatementContext().getStatementName(), entry.getKey(), entry.getValue()); } return new ContextControllerStatementCtxCacheFilters(streamAnalysis.getFilters()); } public void populateFilterAddendums(IdentityHashMap<FilterSpecCompiled, 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 ContextPartitionIdentifier keyPayloadToIdentifier(Object payload) { return new ContextPartitionIdentifierHash((Integer) payload); } 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().getEngineImportService().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().getEngineImportService(), item.getFilterSpecCompiled().getFilterForEventType(), factoryContext.getAgentInstanceContextCreate().getStatementContext().getEventAdapterService(), factoryContext.getAgentInstanceContextCreate().getStatementId(), factoryContext.getServicesContext().getTableService(), factoryContext.getServicesContext().getEngineURI()); } else { throw new IllegalArgumentException("Unrecognized hash code function '" + hashFuncName + "'"); } // create and register expression String expression = item.getFunction().getName() + "(" + ExprNodeUtility.toExpressionStringMinPrecedenceSafe(paramExpr) + ")"; FilterSpecLookupable lookupable = new FilterSpecLookupable(expression, getter, Integer.class, true); item.setLookupable(lookupable); factoryContext.getServicesContext().getFilterNonPropertyRegisteryService().registerNonPropertyExpression(factoryContext.getAgentInstanceContextCreate().getStatementName(), item.getFilterSpecCompiled().getFilterForEventType(), lookupable); nonPropertyExpressions.put(item.getFilterSpecCompiled().getFilterForEventType(), lookupable); } } // Compare filters in statement with filters in segmented context, addendum filter compilation public static void getAddendumFilters(IdentityHashMap<FilterSpecCompiled, FilterValueSetParam[][]> addendums, int hashCode, List<FilterSpecCompiled> filtersSpecs, ContextDetailHash hashSpec, ContextControllerStatementDesc statementDesc) { for (FilterSpecCompiled filtersSpec : filtersSpecs) { FilterValueSetParam[][] addendum = getAddendumFilters(filtersSpec, hashCode, hashSpec, statementDesc); if (addendum == null) { continue; } FilterValueSetParam[][] existing = addendums.get(filtersSpec); if (existing != null) { addendum = ContextControllerAddendumUtil.multiplyAddendum(existing, addendum); } addendums.put(filtersSpec, addendum); } } public static FilterValueSetParam[][] getAddendumFilters(FilterSpecCompiled filterSpecCompiled, int hashCode, ContextDetailHash hashSpec, ContextControllerStatementDesc statementDesc) { // determine whether create-named-window boolean isCreateWindow = statementDesc != null && statementDesc.getStatement().getStatementSpec().getCreateWindowDesc() != null; ContextDetailHashItem foundPartition = null; if (!isCreateWindow) { foundPartition = findHashItemSpec(hashSpec, filterSpecCompiled); } else { String declaredAsName = statementDesc.getStatement().getStatementSpec().getCreateWindowDesc().getAsEventTypeName(); for (ContextDetailHashItem partitionItem : hashSpec.getItems()) { if (partitionItem.getFilterSpecCompiled().getFilterForEventType().getName().equals(declaredAsName)) { foundPartition = partitionItem; break; } } } if (foundPartition == null) { return null; } FilterValueSetParam filter = new FilterValueSetParamImpl(foundPartition.getLookupable(), FilterOperator.EQUAL, hashCode); FilterValueSetParam[][] addendum = new FilterValueSetParam[1][]; addendum[0] = new FilterValueSetParam[]{filter}; FilterValueSetParam[][] partitionFilters = foundPartition.getParametersCompiled(); if (partitionFilters != null) { addendum = ContextControllerAddendumUtil.addAddendum(partitionFilters, filter); } return addendum; } 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(Locale.ENGLISH).trim(); for (HashFunctionEnum val : HashFunctionEnum.values()) { if (val.name().toLowerCase(Locale.ENGLISH).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(Locale.ENGLISH).trim()); delimiter = ", "; } return message.toString(); } } }