com.espertech.esper.epl.named.NamedWindowIndexRepository.java Source code

Java tutorial

Introduction

Here is the source code for com.espertech.esper.epl.named.NamedWindowIndexRepository.java

Source

/**************************************************************************************
 * 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.named;

import com.espertech.esper.client.EPException;
import com.espertech.esper.client.EventBean;
import com.espertech.esper.client.EventType;
import com.espertech.esper.collection.Pair;
import com.espertech.esper.epl.join.hint.*;
import com.espertech.esper.epl.join.plan.QueryPlanIndexItem;
import com.espertech.esper.epl.join.table.EventTable;
import com.espertech.esper.epl.join.table.EventTableAndNamePair;
import com.espertech.esper.epl.join.table.EventTableUtil;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.util.*;

/**
 * A repository of index tables for use with a single named window and all it's deleting statements that
 * may use the indexes to correlate triggering events with indexed events of the named window.
 * <p>
 * Maintains index tables and keeps a reference count for user. Allows reuse of indexes for multiple
 * deleting statements.
 */
public class NamedWindowIndexRepository {
    private static final Log log = LogFactory.getLog(NamedWindowIndexRepository.class);

    private List<EventTable> tables;
    private Map<IndexMultiKey, NamedWindowIndexRepEntry> tableIndexesRefCount;

    /**
     * Ctor.
     */
    public NamedWindowIndexRepository() {
        tables = new ArrayList<EventTable>();
        tableIndexesRefCount = new HashMap<IndexMultiKey, NamedWindowIndexRepEntry>();
    }

    public Pair<IndexMultiKey, EventTableAndNamePair> addExplicitIndexOrReuse(boolean unique,
            List<IndexedPropDesc> hashProps, List<IndexedPropDesc> btreeProps, Iterable<EventBean> prefilledEvents,
            EventType indexedType, String indexName) {
        if (hashProps.isEmpty() && btreeProps.isEmpty()) {
            throw new IllegalArgumentException("Invalid zero element list for hash and btree columns");
        }

        // Get an existing table, if any, matching the exact requirement
        IndexMultiKey indexPropKeyMatch = findExactMatchNameAndType(tableIndexesRefCount.keySet(), unique,
                hashProps, btreeProps);
        if (indexPropKeyMatch != null) {
            NamedWindowIndexRepEntry refTablePair = tableIndexesRefCount.get(indexPropKeyMatch);
            refTablePair.setRefCount(refTablePair.getRefCount() + 1);
            return new Pair<IndexMultiKey, EventTableAndNamePair>(indexPropKeyMatch,
                    new EventTableAndNamePair(refTablePair.getTable(), refTablePair.getOptionalIndexName()));
        }

        return addIndex(unique, hashProps, btreeProps, prefilledEvents, indexedType, indexName, false);
    }

    public Pair<IndexMultiKey, EventTableAndNamePair> addTableCreateOrReuse(List<IndexedPropDesc> hashProps,
            List<IndexedPropDesc> btreeProps, Iterable<EventBean> prefilledEvents, EventType indexedType,
            IndexHint optionalIndexHint, boolean isIndexShare, int subqueryNumber,
            Set<String> optionalUniqueKeyProps) {
        if (hashProps.isEmpty() && btreeProps.isEmpty()) {
            throw new IllegalArgumentException("Invalid zero element list for hash and btree columns");
        }

        // if there are hints, follow these
        if (optionalIndexHint != null) {
            Map<IndexMultiKey, NamedWindowIndexRepEntry> indexCandidates = findCandidates(hashProps, btreeProps);
            List<IndexHintInstruction> instructions = optionalIndexHint.getInstructionsSubquery(subqueryNumber);
            IndexMultiKey found = handleIndexHint(indexCandidates, instructions);
            if (found != null) {
                return reference(found);
            }
        }

        // Get an existing table, if any, matching the exact requirement, prefer unique
        IndexMultiKey indexPropKeyMatch = findExactMatchNameAndType(tableIndexesRefCount.keySet(), true, hashProps,
                btreeProps);
        if (indexPropKeyMatch == null) {
            indexPropKeyMatch = findExactMatchNameAndType(tableIndexesRefCount.keySet(), false, hashProps,
                    btreeProps);
        }
        if (indexPropKeyMatch != null) {
            return reference(indexPropKeyMatch);
        }

        // not found as a full match
        // try match on any of the unique indexes
        Map<IndexMultiKey, NamedWindowIndexRepEntry> indexCandidates = findCandidates(hashProps, btreeProps);
        if (!indexCandidates.isEmpty()) {
            for (Map.Entry<IndexMultiKey, NamedWindowIndexRepEntry> indexKey : indexCandidates.entrySet()) {
                if (indexKey.getKey().isUnique()) {
                    return reference(indexKey.getKey());
                }
            }
        }

        // not found, see if the named window is declared unique
        boolean unique = false;
        boolean coerce = !isIndexShare;
        if (optionalUniqueKeyProps != null && !optionalUniqueKeyProps.isEmpty()) {
            List<IndexedPropDesc> newHashProps = new ArrayList<IndexedPropDesc>();
            for (String uniqueKey : optionalUniqueKeyProps) {
                boolean found = false;
                for (IndexedPropDesc hashProp : hashProps) {
                    if (hashProp.getIndexPropName().equals(uniqueKey)) {
                        newHashProps.add(hashProp);
                        found = true;
                        break;
                    }
                }
                if (!found) {
                    newHashProps = null;
                    break;
                }
            }
            if (newHashProps != null) {
                hashProps = newHashProps;
                btreeProps = Collections.emptyList();
                unique = true;
                coerce = false;
            }
        }

        // not found at all, create
        return addIndex(unique, hashProps, btreeProps, prefilledEvents, indexedType, null, coerce);
    }

    private IndexMultiKey handleIndexHint(Map<IndexMultiKey, NamedWindowIndexRepEntry> indexCandidates,
            List<IndexHintInstruction> instructions) {
        for (IndexHintInstruction instruction : instructions) {
            if (instruction instanceof IndexHintInstructionIndexName) {
                String indexName = ((IndexHintInstructionIndexName) instruction).getIndexName();
                IndexMultiKey found = findExplicitIndexByName(indexCandidates, indexName);
                if (found != null) {
                    return found;
                }
            }
            if (instruction instanceof IndexHintInstructionExplicit) {
                IndexMultiKey found = findExplicitIndexAnyName(indexCandidates);
                if (found != null) {
                    return found;
                }
            }
            if (instruction instanceof IndexHintInstructionBust) {
                throw new EPException("Failed to plan index access, index hint busted out");
            }
        }
        return null;
    }

    private Pair<IndexMultiKey, EventTableAndNamePair> reference(IndexMultiKey found) {
        NamedWindowIndexRepEntry refTablePair = tableIndexesRefCount.get(found);
        refTablePair.setRefCount(refTablePair.getRefCount() + 1);
        return new Pair<IndexMultiKey, EventTableAndNamePair>(found,
                new EventTableAndNamePair(refTablePair.getTable(), refTablePair.getOptionalIndexName()));
    }

    private IndexMultiKey findExplicitIndexByName(Map<IndexMultiKey, NamedWindowIndexRepEntry> indexCandidates,
            String name) {
        for (Map.Entry<IndexMultiKey, NamedWindowIndexRepEntry> entry : indexCandidates.entrySet()) {
            if (entry.getValue().getOptionalIndexName() != null
                    && entry.getValue().getOptionalIndexName().equals(name)) {
                return entry.getKey();
            }
        }
        return null;
    }

    private IndexMultiKey findExplicitIndexAnyName(Map<IndexMultiKey, NamedWindowIndexRepEntry> indexCandidates) {
        for (Map.Entry<IndexMultiKey, NamedWindowIndexRepEntry> entry : indexCandidates.entrySet()) {
            if (entry.getValue().getOptionalIndexName() != null) {
                return entry.getKey();
            }
        }
        return null;
    }

    private Map<IndexMultiKey, NamedWindowIndexRepEntry> findCandidates(List<IndexedPropDesc> hashProps,
            List<IndexedPropDesc> btreeProps) {
        Map<IndexMultiKey, NamedWindowIndexRepEntry> indexCandidates = new HashMap<IndexMultiKey, NamedWindowIndexRepEntry>();
        for (Map.Entry<IndexMultiKey, NamedWindowIndexRepEntry> entry : tableIndexesRefCount.entrySet()) {
            boolean matches = indexMatchesProvided(entry.getKey(), hashProps, btreeProps);
            if (matches) {
                indexCandidates.put(entry.getKey(), entry.getValue());
            }
        }
        return indexCandidates;
    }

    private Pair<IndexMultiKey, EventTableAndNamePair> addIndex(boolean unique, List<IndexedPropDesc> hashProps,
            List<IndexedPropDesc> btreeProps, Iterable<EventBean> prefilledEvents, EventType indexedType,
            String indexName, boolean mustCoerce) {

        // not resolved as full match and not resolved as unique index match, allocate
        IndexMultiKey indexPropKey = new IndexMultiKey(unique, hashProps, btreeProps);

        IndexedPropDesc[] indexedPropDescs = hashProps.toArray(new IndexedPropDesc[hashProps.size()]);
        String[] indexProps = IndexedPropDesc.getIndexProperties(indexedPropDescs);
        Class[] indexCoercionTypes = IndexedPropDesc.getCoercionTypes(indexedPropDescs);
        if (!mustCoerce) {
            indexCoercionTypes = null;
        }

        IndexedPropDesc[] rangePropDescs = btreeProps.toArray(new IndexedPropDesc[btreeProps.size()]);
        String[] rangeProps = IndexedPropDesc.getIndexProperties(rangePropDescs);
        Class[] rangeCoercionTypes = IndexedPropDesc.getCoercionTypes(rangePropDescs);

        QueryPlanIndexItem indexItem = new QueryPlanIndexItem(indexProps, indexCoercionTypes, rangeProps,
                rangeCoercionTypes, false);
        EventTable table = EventTableUtil.buildIndex(0, indexItem, indexedType, true, unique, indexName);

        // fill table since its new
        EventBean[] events = new EventBean[1];
        for (EventBean prefilledEvent : prefilledEvents) {
            events[0] = prefilledEvent;
            table.add(events);
        }

        // add table
        tables.add(table);

        // add index, reference counted
        tableIndexesRefCount.put(indexPropKey, new NamedWindowIndexRepEntry(table, indexName, 1));

        return new Pair<IndexMultiKey, EventTableAndNamePair>(indexPropKey,
                new EventTableAndNamePair(table, indexName));
    }

    private boolean indexMatchesProvided(IndexMultiKey indexDesc, List<IndexedPropDesc> hashPropsProvided,
            List<IndexedPropDesc> rangePropsProvided) {
        IndexedPropDesc[] hashPropIndexedList = indexDesc.getHashIndexedProps();
        for (IndexedPropDesc hashPropIndexed : hashPropIndexedList) {
            boolean foundHashProp = indexHashIsProvided(hashPropIndexed, hashPropsProvided);
            if (!foundHashProp) {
                return false;
            }
        }

        IndexedPropDesc[] rangePropIndexedList = indexDesc.getRangeIndexedProps();
        for (IndexedPropDesc rangePropIndexed : rangePropIndexedList) {
            boolean foundRangeProp = indexHashIsProvided(rangePropIndexed, rangePropsProvided);
            if (!foundRangeProp) {
                return false;
            }
        }

        return true;
    }

    private boolean indexHashIsProvided(IndexedPropDesc hashPropIndexed, List<IndexedPropDesc> hashPropsProvided) {
        for (IndexedPropDesc hashPropProvided : hashPropsProvided) {
            if (hashPropProvided.getIndexPropName().equals(hashPropIndexed.getIndexPropName())) {
                return true;
            }
        }
        return false;
    }

    private IndexMultiKey findExactMatchNameAndType(Set<IndexMultiKey> indexMultiKeys, boolean unique,
            List<IndexedPropDesc> hashProps, List<IndexedPropDesc> btreeProps) {
        for (IndexMultiKey existing : indexMultiKeys) {
            if (isExactMatch(existing, unique, hashProps, btreeProps)) {
                return existing;
            }
        }
        return null;
    }

    private boolean isExactMatch(IndexMultiKey existing, boolean unique, List<IndexedPropDesc> hashProps,
            List<IndexedPropDesc> btreeProps) {
        if (existing.isUnique() != unique) {
            return false;
        }
        boolean keyPropCompare = IndexedPropDesc.compare(Arrays.asList(existing.getHashIndexedProps()), hashProps);
        return keyPropCompare
                && IndexedPropDesc.compare(Arrays.asList(existing.getRangeIndexedProps()), btreeProps);
    }

    public void addTableReference(EventTable table) {
        for (Map.Entry<IndexMultiKey, NamedWindowIndexRepEntry> entry : tableIndexesRefCount.entrySet()) {
            if (entry.getValue().getTable() == table) {
                int current = entry.getValue().getRefCount() + 1;
                entry.getValue().setRefCount(current);
            }
        }
    }

    /**
     * Remove a reference to an index table, decreasing its reference count.
     * If the table is no longer used, discard it and no longer update events into the index.
     * @param table to remove a reference to
     */
    public void removeTableReference(EventTable table) {
        for (Map.Entry<IndexMultiKey, NamedWindowIndexRepEntry> entry : tableIndexesRefCount.entrySet()) {
            if (entry.getValue().getTable() == table) {
                int current = entry.getValue().getRefCount();
                if (current > 1) {
                    current--;
                    entry.getValue().setRefCount(current);
                    break;
                }

                tables.remove(table);
                tableIndexesRefCount.remove(entry.getKey());
                break;
            }
        }
    }

    /**
     * Returns a list of current index tables in the repository.
     * @return index tables
     */
    public List<EventTable> getTables() {
        return tables;
    }

    /**
     * Destroy indexes.
     */
    public void destroy() {
        tables.clear();
        tableIndexesRefCount.clear();
    }

    public Pair<IndexMultiKey, EventTableAndNamePair> findTable(Set<String> keyPropertyNames,
            Set<String> rangePropertyNames, Map<String, EventTable> explicitIndexNames,
            IndexHint optionalIndexHint) {

        if (keyPropertyNames.isEmpty() && rangePropertyNames.isEmpty()) {
            return null;
        }

        // determine candidates
        List<IndexedPropDesc> hashProps = new ArrayList<IndexedPropDesc>();
        for (String keyPropertyName : keyPropertyNames) {
            hashProps.add(new IndexedPropDesc(keyPropertyName, null));
        }
        List<IndexedPropDesc> rangeProps = new ArrayList<IndexedPropDesc>();
        for (String rangePropertyName : rangePropertyNames) {
            rangeProps.add(new IndexedPropDesc(rangePropertyName, null));
        }
        Map<IndexMultiKey, NamedWindowIndexRepEntry> indexCandidates = findCandidates(hashProps, rangeProps);

        // handle hint
        if (optionalIndexHint != null) {
            List<IndexHintInstruction> instructions = optionalIndexHint.getInstructionsFireAndForget();
            IndexMultiKey found = handleIndexHint(indexCandidates, instructions);
            if (found != null) {
                return getPair(found);
            }
        }

        // no candidates
        if (indexCandidates == null || indexCandidates.isEmpty()) {
            if (log.isDebugEnabled()) {
                log.debug("No index found.");
            }
            return null;
        }

        // take the table that has a unique index
        for (Map.Entry<IndexMultiKey, NamedWindowIndexRepEntry> entry : indexCandidates.entrySet()) {
            if (entry.getKey().isUnique()) {
                return getPair(entry.getKey());
            }
        }

        // take the best available table
        IndexMultiKey indexMultiKey;
        List<IndexMultiKey> indexes = new ArrayList<IndexMultiKey>(indexCandidates.keySet());
        if (indexes.size() > 1) {
            Comparator<IndexMultiKey> comparator = new Comparator<IndexMultiKey>() {
                public int compare(IndexMultiKey o1, IndexMultiKey o2) {
                    String[] indexedProps1 = IndexedPropDesc.getIndexProperties(o1.getHashIndexedProps());
                    String[] indexedProps2 = IndexedPropDesc.getIndexProperties(o2.getHashIndexedProps());
                    if (indexedProps1.length > indexedProps2.length) {
                        return -1; // sort desc by count columns
                    }
                    if (indexedProps1.length == indexedProps2.length) {
                        return 0;
                    }
                    return 1;
                }
            };
            Collections.sort(indexes, comparator);
        }
        indexMultiKey = indexes.get(0);
        return getPair(indexMultiKey);
    }

    private Pair<IndexMultiKey, EventTableAndNamePair> getPair(IndexMultiKey indexMultiKey) {
        NamedWindowIndexRepEntry indexFound = tableIndexesRefCount.get(indexMultiKey);
        EventTable tableFound = indexFound.getTable();
        return new Pair<IndexMultiKey, EventTableAndNamePair>(indexMultiKey,
                new EventTableAndNamePair(tableFound, indexFound.getOptionalIndexName()));
    }

    public IndexMultiKey[] getIndexDescriptors() {
        Set<IndexMultiKey> keySet = tableIndexesRefCount.keySet();
        return keySet.toArray(new IndexMultiKey[keySet.size()]);
    }
}