com.espertech.esper.core.InternalEventRouterImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.espertech.esper.core.InternalEventRouterImpl.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.core;

import com.espertech.esper.client.EventBean;
import com.espertech.esper.client.EventPropertyDescriptor;
import com.espertech.esper.client.EventType;
import com.espertech.esper.client.annotation.Drop;
import com.espertech.esper.client.annotation.Priority;
import com.espertech.esper.epl.expression.ExprEvaluatorContext;
import com.espertech.esper.epl.expression.ExprNode;
import com.espertech.esper.epl.expression.ExprValidationException;
import com.espertech.esper.epl.spec.OnTriggerSetAssignment;
import com.espertech.esper.epl.spec.UpdateDesc;
import com.espertech.esper.event.EventBeanCopyMethod;
import com.espertech.esper.event.EventBeanWriter;
import com.espertech.esper.event.EventTypeSPI;
import com.espertech.esper.util.NullableObject;
import com.espertech.esper.util.TypeWidener;
import com.espertech.esper.util.TypeWidenerFactory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.lang.annotation.Annotation;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;

/**
 * Routing implementation that allows to pre-process events.
 */
public class InternalEventRouterImpl implements InternalEventRouter {
    private static final Log log = LogFactory.getLog(InternalEventRouterImpl.class);

    private final ConcurrentHashMap<EventType, NullableObject<InternalEventRouterPreprocessor>> preprocessors;
    private final Map<UpdateDesc, IRDescEntry> descriptors;
    private boolean hasPreprocessing = false;
    private InsertIntoListener insertIntoListener;

    /**
     * Ctor.
     */
    public InternalEventRouterImpl() {
        this.preprocessors = new ConcurrentHashMap<EventType, NullableObject<InternalEventRouterPreprocessor>>();
        this.descriptors = new LinkedHashMap<UpdateDesc, IRDescEntry>();
    }

    /**
     * Return true to indicate that there is pre-processing to take place.
     * @return preprocessing indicator
     */
    public boolean isHasPreprocessing() {
        return hasPreprocessing;
    }

    /**
     * Pre-process the event.
     * @param eventBean to preprocess
     * @param exprEvaluatorContext expression evaluation context
     * @return preprocessed event
     */
    public EventBean preprocess(EventBean eventBean, ExprEvaluatorContext exprEvaluatorContext) {
        return getPreprocessedEvent(eventBean, exprEvaluatorContext);
    }

    public void setInsertIntoListener(InsertIntoListener insertIntoListener) {
        this.insertIntoListener = insertIntoListener;
    }

    public void route(EventBean event, EPStatementHandle statementHandle, InternalEventRouteDest routeDest,
            ExprEvaluatorContext exprEvaluatorContext, boolean addToFront) {
        if (!hasPreprocessing) {
            if (insertIntoListener != null) {
                insertIntoListener.inserted(event, statementHandle);
            }
            routeDest.route(event, statementHandle, addToFront);
            return;
        }

        EventBean preprocessed = getPreprocessedEvent(event, exprEvaluatorContext);
        if (preprocessed != null) {
            if (insertIntoListener != null) {
                insertIntoListener.inserted(event, statementHandle);
            }
            routeDest.route(preprocessed, statementHandle, addToFront);
        }
    }

    public void addPreprocessing(EventType eventType, UpdateDesc desc, Annotation[] annotations,
            InternalRoutePreprocessView outputView) throws ExprValidationException {
        if (log.isInfoEnabled()) {
            log.info("Adding route preprocessing for type '" + eventType.getName() + "'");
        }

        if (!(eventType instanceof EventTypeSPI)) {
            throw new ExprValidationException("Update statements require the event type to implement the "
                    + EventTypeSPI.class + " interface");
        }
        EventTypeSPI eventTypeSPI = (EventTypeSPI) eventType;

        TypeWidener[] wideners = new TypeWidener[desc.getAssignments().size()];
        List<String> properties = new ArrayList<String>();
        for (int i = 0; i < desc.getAssignments().size(); i++) {
            OnTriggerSetAssignment assignment = desc.getAssignments().get(i);
            EventPropertyDescriptor writableProperty = eventTypeSPI
                    .getWritableProperty(assignment.getVariableName());

            if (writableProperty == null) {
                throw new ExprValidationException(
                        "Property '" + assignment.getVariableName() + "' is not available for write access");
            }

            wideners[i] = TypeWidenerFactory.getCheckPropertyAssignType(
                    assignment.getExpression().toExpressionString(),
                    assignment.getExpression().getExprEvaluator().getType(), writableProperty.getPropertyType(),
                    assignment.getVariableName());
            properties.add(assignment.getVariableName());
        }

        // check copy-able
        EventBeanCopyMethod copyMethod = eventTypeSPI
                .getCopyMethod(properties.toArray(new String[properties.size()]));
        if (copyMethod == null) {
            throw new ExprValidationException(
                    "The update-clause requires the underlying event representation to support copy (via Serializable by default)");
        }

        descriptors.put(desc, new IRDescEntry(eventType, annotations, wideners, outputView));

        // remove all preprocessors for this type as well as any known child types, forcing re-init on next use
        removePreprocessors(eventType);

        hasPreprocessing = true;
    }

    public void removePreprocessing(EventType eventType, UpdateDesc desc) {
        if (log.isInfoEnabled()) {
            log.info("Removing route preprocessing for type '" + eventType.getName());
        }

        // remove all preprocessors for this type as well as any known child types
        removePreprocessors(eventType);

        descriptors.remove(desc);
        if (descriptors.isEmpty()) {
            hasPreprocessing = false;
            preprocessors.clear();
        }
    }

    private EventBean getPreprocessedEvent(EventBean event, ExprEvaluatorContext exprEvaluatorContext) {
        NullableObject<InternalEventRouterPreprocessor> processor = preprocessors.get(event.getEventType());
        if (processor == null) {
            synchronized (this) {
                processor = initialize(event.getEventType());
                preprocessors.put(event.getEventType(), processor);
            }
        }

        if (processor.getObject() == null) {
            return event;
        } else {
            return processor.getObject().process(event, exprEvaluatorContext);
        }
    }

    private void removePreprocessors(EventType eventType) {
        preprocessors.remove(eventType);

        // find each child type entry
        for (EventType type : preprocessors.keySet()) {
            if (type.getDeepSuperTypes() != null) {
                for (Iterator<EventType> it = type.getDeepSuperTypes(); it.hasNext();) {
                    if (it.next() == eventType) {
                        preprocessors.remove(type);
                    }
                }
            }
        }
    }

    private NullableObject<InternalEventRouterPreprocessor> initialize(EventType eventType) {
        EventTypeSPI eventTypeSPI = (EventTypeSPI) eventType;
        List<InternalEventRouterEntry> desc = new ArrayList<InternalEventRouterEntry>();

        // determine which ones to process for this types, and what priority and drop
        Set<String> eventPropertiesWritten = new HashSet<String>();
        for (Map.Entry<UpdateDesc, IRDescEntry> entry : descriptors.entrySet()) {
            boolean applicable = entry.getValue().getEventType() == eventType;
            if (!applicable) {
                if (eventType.getDeepSuperTypes() != null) {
                    for (Iterator<EventType> it = eventType.getDeepSuperTypes(); it.hasNext();) {
                        if (it.next() == entry.getValue().getEventType()) {
                            applicable = true;
                            break;
                        }
                    }
                }
            }

            if (!applicable) {
                continue;
            }

            int priority = 0;
            boolean isDrop = false;
            Annotation[] annotations = entry.getValue().getAnnotations();
            for (int i = 0; i < annotations.length; i++) {
                if (annotations[i] instanceof Priority) {
                    priority = ((Priority) annotations[i]).value();
                }
                if (annotations[i] instanceof Drop) {
                    isDrop = true;
                }
            }

            List<String> properties = new ArrayList<String>();
            ExprNode[] expressions = new ExprNode[entry.getKey().getAssignments().size()];
            for (int i = 0; i < entry.getKey().getAssignments().size(); i++) {
                OnTriggerSetAssignment assignment = entry.getKey().getAssignments().get(i);
                expressions[i] = assignment.getExpression();
                properties.add(assignment.getVariableName());
                eventPropertiesWritten.add(assignment.getVariableName());
            }
            EventBeanWriter writer = eventTypeSPI.getWriter(properties.toArray(new String[properties.size()]));
            desc.add(new InternalEventRouterEntry(priority, isDrop, entry.getKey().getOptionalWhereClause(),
                    expressions, writer, entry.getValue().getWideners(), entry.getValue().getOutputView()));
        }

        EventBeanCopyMethod copyMethod = eventTypeSPI
                .getCopyMethod(eventPropertiesWritten.toArray(new String[eventPropertiesWritten.size()]));
        if (copyMethod == null) {
            return new NullableObject<InternalEventRouterPreprocessor>(null);
        }
        return new NullableObject<InternalEventRouterPreprocessor>(
                new InternalEventRouterPreprocessor(copyMethod, desc));
    }

    private static class IRDescEntry {
        private EventType eventType;
        private Annotation[] annotations;
        private TypeWidener[] wideners;
        private InternalRoutePreprocessView outputView;

        public IRDescEntry(EventType eventType, Annotation[] annotations, TypeWidener[] wideners,
                InternalRoutePreprocessView outputView) {
            this.eventType = eventType;
            this.annotations = annotations;
            this.wideners = wideners;
            this.outputView = outputView;
        }

        public EventType getEventType() {
            return eventType;
        }

        public Annotation[] getAnnotations() {
            return annotations;
        }

        public TypeWidener[] getWideners() {
            return wideners;
        }

        public InternalRoutePreprocessView getOutputView() {
            return outputView;
        }
    }
}