com.espertech.esper.filter.FilterServiceBase.java Source code

Java tutorial

Introduction

Here is the source code for com.espertech.esper.filter.FilterServiceBase.java

Source

/**************************************************************************************
 * Copyright (C) 2006-2015 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.filter;

import com.espertech.esper.client.EventBean;
import com.espertech.esper.client.EventType;
import com.espertech.esper.metrics.instrumentation.InstrumentationHelper;
import com.espertech.esper.metrics.jmx.JmxGetter;
import com.espertech.esper.metrics.jmx.JmxOperation;
import com.espertech.esper.util.AuditPath;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.util.ArrayDeque;
import java.util.Collection;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.LockSupport;

/**
 * Implementation of the filter service interface.
 * Does not allow the same filter callback to be added more then once.
 */
public abstract class FilterServiceBase implements FilterServiceSPI {
    private final FilterServiceGranularLockFactory lockFactory;
    private static final Log log = LogFactory.getLog(FilterServiceBase.class);
    private final EventTypeIndexBuilder indexBuilder;
    private final EventTypeIndex eventTypeIndex;
    private final AtomicLong numEventsEvaluated = new AtomicLong();
    private volatile long filtersVersion = 1;
    private final CopyOnWriteArraySet<FilterServiceListener> filterServiceListeners;

    /**
     * Constructor.
     */
    protected FilterServiceBase(FilterServiceGranularLockFactory lockFactory, boolean allowIsolation) {
        this.lockFactory = lockFactory;
        eventTypeIndex = new EventTypeIndex(lockFactory);
        indexBuilder = new EventTypeIndexBuilder(eventTypeIndex, allowIsolation);
        filterServiceListeners = new CopyOnWriteArraySet<FilterServiceListener>();
    }

    public long getFiltersVersion() {
        return filtersVersion;
    }

    public void destroy() {
        log.debug("Destroying filter service");
        eventTypeIndex.destroy();
        indexBuilder.destroy();
    }

    protected FilterServiceEntry addInternal(FilterValueSet filterValueSet, FilterHandle filterCallback) {
        FilterServiceEntry entry = indexBuilder.add(filterValueSet, filterCallback, lockFactory);
        filtersVersion++;
        return entry;
    }

    protected void removeInternal(FilterHandle filterCallback, FilterServiceEntry filterServiceEntry) {
        indexBuilder.remove(filterCallback, filterServiceEntry);
        filtersVersion++;
    }

    protected long evaluateInternal(EventBean theEvent, Collection<FilterHandle> matches) {
        if (InstrumentationHelper.ENABLED) {
            InstrumentationHelper.get().qFilter(theEvent);
        }

        long version = filtersVersion;
        numEventsEvaluated.incrementAndGet();

        // Finds all matching filters and return their callbacks.
        retryableMatchEvent(theEvent, matches);

        if ((AuditPath.isAuditEnabled) && (!filterServiceListeners.isEmpty())) {
            for (FilterServiceListener listener : filterServiceListeners) {
                listener.filtering(theEvent, matches, null);
            }
        }

        if (InstrumentationHelper.ENABLED) {
            InstrumentationHelper.get().aFilter(matches);
        }

        return version;
    }

    protected long evaluateInternal(EventBean theEvent, Collection<FilterHandle> matches, String statementId) {
        long version = filtersVersion;
        numEventsEvaluated.incrementAndGet();

        ArrayDeque<FilterHandle> allMatches = new ArrayDeque<FilterHandle>();

        // Finds all matching filters
        retryableMatchEvent(theEvent, allMatches);

        // Add statement matches to collection passed
        for (FilterHandle match : allMatches) {
            if (match.getStatementId().equals(statementId)) {
                matches.add(match);
            }
        }

        if ((AuditPath.isAuditEnabled) && (!filterServiceListeners.isEmpty())) {
            for (FilterServiceListener listener : filterServiceListeners) {
                listener.filtering(theEvent, matches, statementId);
            }
        }

        return version;
    }

    @JmxGetter(name = "NumEventsEvaluated", description = "Number of events evaluated (main)")
    public final long getNumEventsEvaluated() {
        return numEventsEvaluated.get();
    }

    @JmxOperation(description = "Reset number of events evaluated")
    public void resetStats() {
        numEventsEvaluated.set(0);
    }

    public void addFilterServiceListener(FilterServiceListener filterServiceListener) {
        filterServiceListeners.add(filterServiceListener);
    }

    public void removeFilterServiceListener(FilterServiceListener filterServiceListener) {
        filterServiceListeners.remove(filterServiceListener);
    }

    protected FilterSet takeInternal(Set<String> statementIds) {
        filtersVersion++;
        return indexBuilder.take(statementIds);
    }

    protected void applyInternal(FilterSet filterSet) {
        filtersVersion++;
        indexBuilder.apply(filterSet, lockFactory);
    }

    @JmxGetter(name = "NumFiltersApprox", description = "Number of filters managed (approximately)")
    public int getFilterCountApprox() {
        return eventTypeIndex.getFilterCountApprox();
    }

    @JmxGetter(name = "NumEventTypes", description = "Number of event types considered")
    public int getCountTypes() {
        return eventTypeIndex.size();
    }

    public void init() {
        // no initialization required
    }

    protected void removeTypeInternal(EventType type) {
        eventTypeIndex.removeType(type);
    }

    private void retryableMatchEvent(EventBean theEvent, Collection<FilterHandle> matches) {
        // Install lock backoff exception handler that retries the evaluation.
        try {
            eventTypeIndex.matchEvent(theEvent, matches);
        } catch (FilterLockBackoffException ex) {
            // retry on lock back-off
            // lock-backoff may occur when stateful evaluations take place such as boolean expressions that are subqueries
            // statements that contain subqueries in pattern filter expression can themselves modify filters, leading to a theoretically possible deadlock
            long delayNs = 10;
            while (true) {
                try {
                    // yield
                    try {
                        Thread.sleep(0);
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                    }

                    // delay
                    LockSupport.parkNanos(delayNs);
                    if (delayNs < 1000000000) {
                        delayNs = delayNs * 2;
                    }

                    // evaluate
                    matches.clear();
                    eventTypeIndex.matchEvent(theEvent, matches);
                    break;
                } catch (FilterLockBackoffException ex2) {
                    // retried
                }
            }
        }
    }
}