com.espertech.esper.view.std.GroupByViewReclaimAged.java Source code

Java tutorial

Introduction

Here is the source code for com.espertech.esper.view.std.GroupByViewReclaimAged.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.view.std;

import com.espertech.esper.client.EventBean;
import com.espertech.esper.client.EventType;
import com.espertech.esper.collection.MultiKeyUntyped;
import com.espertech.esper.collection.Pair;
import com.espertech.esper.core.context.util.AgentInstanceViewFactoryChainContext;
import com.espertech.esper.epl.expression.ExprEvaluator;
import com.espertech.esper.epl.expression.ExprNode;
import com.espertech.esper.util.ExecutionPathDebugLog;
import com.espertech.esper.view.CloneableView;
import com.espertech.esper.view.StoppableView;
import com.espertech.esper.view.View;
import com.espertech.esper.view.ViewSupport;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.util.*;

public class GroupByViewReclaimAged extends ViewSupport implements CloneableView, GroupByView {
    private final ExprNode[] criteriaExpressions;
    private final ExprEvaluator[] criteriaEvaluators;
    protected final AgentInstanceViewFactoryChainContext agentInstanceContext;
    private final long reclaimMaxAge;
    private final long reclaimFrequency;

    private EventBean[] eventsPerStream = new EventBean[1];
    protected String[] propertyNames;

    protected final Map<Object, GroupByViewAgedEntry> subViewsPerKey = new HashMap<Object, GroupByViewAgedEntry>();
    private final HashMap<GroupByViewAgedEntry, Pair<Object, Object>> groupedEvents = new HashMap<GroupByViewAgedEntry, Pair<Object, Object>>();
    private Long nextSweepTime = null;

    /**
     * Constructor.
     * @param agentInstanceContext contains required view services
     * @param criteriaExpressions is the fields from which to pull the values to group by
     * @param reclaimMaxAge age after which to reclaim group
     * @param reclaimFrequency frequency in which to check for groups to reclaim
     */
    public GroupByViewReclaimAged(AgentInstanceViewFactoryChainContext agentInstanceContext,
            ExprNode[] criteriaExpressions, ExprEvaluator[] criteriaEvaluators, double reclaimMaxAge,
            double reclaimFrequency) {
        this.agentInstanceContext = agentInstanceContext;
        this.criteriaExpressions = criteriaExpressions;
        this.criteriaEvaluators = criteriaEvaluators;
        this.reclaimMaxAge = (long) (reclaimMaxAge * 1000d);
        this.reclaimFrequency = (long) (reclaimFrequency * 1000d);

        propertyNames = new String[criteriaExpressions.length];
        for (int i = 0; i < criteriaExpressions.length; i++) {
            propertyNames[i] = criteriaExpressions[i].toExpressionString();
        }
    }

    public View cloneView() {
        return new GroupByViewReclaimAged(agentInstanceContext, criteriaExpressions, criteriaEvaluators,
                reclaimMaxAge, reclaimFrequency);
    }

    /**
     * Returns the field name that provides the key valie by which to group by.
     * @return field name providing group-by key.
     */
    public ExprNode[] getCriteriaExpressions() {
        return criteriaExpressions;
    }

    public final EventType getEventType() {
        // The schema is the parent view's schema
        return parent.getEventType();
    }

    public final void update(EventBean[] newData, EventBean[] oldData) {
        long currentTime = agentInstanceContext.getTimeProvider().getTime();
        if ((nextSweepTime == null) || (nextSweepTime <= currentTime)) {
            if ((ExecutionPathDebugLog.isDebugEnabled) && (log.isDebugEnabled())) {
                log.debug("Reclaiming groups older then " + reclaimMaxAge + " msec and every " + reclaimFrequency
                        + "msec in frequency");
            }
            nextSweepTime = currentTime + reclaimFrequency;
            sweep(currentTime);
        }

        // Algorithm for single new event
        if ((newData != null) && (oldData == null) && (newData.length == 1)) {
            EventBean theEvent = newData[0];
            EventBean[] newDataToPost = new EventBean[] { theEvent };

            Object groupByValuesKey = getGroupKey(theEvent);

            // Get child views that belong to this group-by value combination
            GroupByViewAgedEntry subViews = this.subViewsPerKey.get(groupByValuesKey);

            // If this is a new group-by value, the list of subviews is null and we need to make clone sub-views
            if (subViews == null) {
                Object subviewsList = GroupByViewImpl.makeSubViews(this, propertyNames, groupByValuesKey,
                        agentInstanceContext);
                subViews = new GroupByViewAgedEntry(subviewsList, currentTime);
                subViewsPerKey.put(groupByValuesKey, subViews);
            } else {
                subViews.setLastUpdateTime(currentTime);
            }

            GroupByViewImpl.updateChildViews(subViews.getSubviewHolder(), newDataToPost, null);
        } else {

            // Algorithm for dispatching multiple events
            if (newData != null) {
                for (EventBean newValue : newData) {
                    handleEvent(newValue, true);
                }
            }

            if (oldData != null) {
                for (EventBean oldValue : oldData) {
                    handleEvent(oldValue, false);
                }
            }

            // Update child views
            for (Map.Entry<GroupByViewAgedEntry, Pair<Object, Object>> entry : groupedEvents.entrySet()) {
                EventBean[] newEvents = GroupByViewImpl.convertToArray(entry.getValue().getFirst());
                EventBean[] oldEvents = GroupByViewImpl.convertToArray(entry.getValue().getSecond());
                GroupByViewImpl.updateChildViews(entry.getKey(), newEvents, oldEvents);
            }

            groupedEvents.clear();
        }
    }

    private void handleEvent(EventBean theEvent, boolean isNew) {
        Object groupByValuesKey = getGroupKey(theEvent);

        // Get child views that belong to this group-by value combination
        GroupByViewAgedEntry subViews = this.subViewsPerKey.get(groupByValuesKey);

        // If this is a new group-by value, the list of subviews is null and we need to make clone sub-views
        if (subViews == null) {
            Object subviewsList = GroupByViewImpl.makeSubViews(this, propertyNames, groupByValuesKey,
                    agentInstanceContext);
            long currentTime = agentInstanceContext.getStatementContext().getTimeProvider().getTime();
            subViews = new GroupByViewAgedEntry(subviewsList, currentTime);
            subViewsPerKey.put(groupByValuesKey, subViews);
        } else {
            subViews.setLastUpdateTime(agentInstanceContext.getStatementContext().getTimeProvider().getTime());
        }

        // Construct a pair of lists to hold the events for the grouped value if not already there
        Pair<Object, Object> pair = groupedEvents.get(subViews);
        if (pair == null) {
            pair = new Pair<Object, Object>(null, null);
            groupedEvents.put(subViews, pair);
        }

        // Add event to a child view event list for later child update that includes new and old events
        if (isNew) {
            pair.setFirst(GroupByViewImpl.addUpgradeToDequeIfPopulated(pair.getFirst(), theEvent));
        } else {
            pair.setSecond(GroupByViewImpl.addUpgradeToDequeIfPopulated(pair.getSecond(), theEvent));
        }
    }

    public final Iterator<EventBean> iterator() {
        throw new UnsupportedOperationException("Cannot iterate over group view, this operation is not supported");
    }

    public final String toString() {
        return this.getClass().getName() + " groupFieldNames=" + Arrays.toString(criteriaExpressions);
    }

    private void sweep(long currentTime) {
        ArrayDeque<Object> removed = new ArrayDeque<Object>();
        for (Map.Entry<Object, GroupByViewAgedEntry> entry : subViewsPerKey.entrySet()) {
            long age = currentTime - entry.getValue().getLastUpdateTime();
            if (age > reclaimMaxAge) {
                removed.add(entry.getKey());
            }
        }

        for (Object key : removed) {
            GroupByViewAgedEntry entry = subViewsPerKey.remove(key);
            Object subviewHolder = entry.getSubviewHolder();
            if (subviewHolder instanceof List) {
                List<View> subviews = (List<View>) subviewHolder;
                for (View view : subviews) {
                    removeSubview(view);
                }
            } else if (subviewHolder instanceof View) {
                removeSubview((View) subviewHolder);
            }
        }
    }

    private void removeSubview(View view) {
        view.setParent(null);
        recursiveMergeViewRemove(view);
        if (view instanceof StoppableView) {
            ((StoppableView) view).stopView();
        }
    }

    private void recursiveMergeViewRemove(View view) {
        for (View child : view.getViews()) {
            if (child instanceof MergeView) {
                MergeView mergeView = (MergeView) child;
                mergeView.removeParentView(view);
            } else {
                if (child instanceof StoppableView) {
                    ((StoppableView) child).stopView();
                }
                if (child.getViews().length > 0) {
                    recursiveMergeViewRemove(child);
                }
            }
        }
    }

    private Object getGroupKey(EventBean theEvent) {
        eventsPerStream[0] = theEvent;
        if (criteriaEvaluators.length == 1) {
            return criteriaEvaluators[0].evaluate(eventsPerStream, true, agentInstanceContext);
        }

        Object[] values = new Object[criteriaEvaluators.length];
        for (int i = 0; i < criteriaEvaluators.length; i++) {
            values[i] = criteriaEvaluators[i].evaluate(eventsPerStream, true, agentInstanceContext);
        }
        return new MultiKeyUntyped(values);
    }

    private static final Log log = LogFactory.getLog(GroupByViewReclaimAged.class);
}