/* * ************************************************************************************* * 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.internal; import com.espertech.esper.client.EventBean; import com.espertech.esper.client.EventType; import com.espertech.esper.core.context.util.AgentInstanceViewFactoryChainContext; import com.espertech.esper.event.EventBeanUtility; import com.espertech.esper.view.*; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; /** * A view that represents an intersection of multiple data windows. * <p> * The view is parameterized by two or more data windows. From an external viewpoint, the * view retains all events that is in all of the data windows at the same time (an intersection) * and removes all events that leave any of the data windows. * <p> * This special batch-version has the following logic: * - only one batching view allowed as sub-view * - all externally-received newData events are inserted into each view * - all externally-received oldData events are removed from each view * - any non-batch view has its newData output ignored * - the single batch-view has its newData posted to child views, and removed from all non-batch views * - all oldData events received from all non-batch views are removed from each view */ public class IntersectBatchView extends ViewSupport implements LastPostObserver, CloneableView, StoppableView { private static final Log log = LogFactory.getLog(IntersectBatchView.class); protected final AgentInstanceViewFactoryChainContext agentInstanceViewFactoryContext; protected final IntersectViewFactory intersectViewFactory; protected final EventType eventType; protected final View[] views; private int batchViewIndex; private final EventBean[][] oldEventsPerView; private final EventBean[][] newEventsPerView; private final HashSet<EventBean> removedEvents = new LinkedHashSet<EventBean>(); protected final boolean hasAsymetric; private boolean captureIRNonBatch; private boolean ignoreViewIRStream; /** * Ctor. * @param factory the view factory * @param eventType the parent event type * @param viewList the list of data window views * @param viewFactories view factories */ public IntersectBatchView(AgentInstanceViewFactoryChainContext agentInstanceViewFactoryContext, IntersectViewFactory factory, EventType eventType, List<View> viewList, List<ViewFactory> viewFactories, boolean hasAsymetric) { this.agentInstanceViewFactoryContext = agentInstanceViewFactoryContext; this.intersectViewFactory = factory; this.eventType = eventType; this.views = viewList.toArray(new View[viewList.size()]); this.oldEventsPerView = new EventBean[viewList.size()][]; this.newEventsPerView = new EventBean[viewList.size()][]; this.hasAsymetric = hasAsymetric; // determine index of batch view batchViewIndex = -1; for (int i = 0; i < viewFactories.size(); i++) { if (viewFactories.get(i) instanceof DataWindowBatchingViewFactory) { batchViewIndex = i; } } if (batchViewIndex == -1) { throw new IllegalStateException("Failed to find batch data window view"); } for (int i = 0; i < viewList.size(); i++) { LastPostObserverView view = new LastPostObserverView(i); views[i].removeAllViews(); views[i].addView(view); view.setObserver(this); } } public View cloneView() { return intersectViewFactory.makeView(agentInstanceViewFactoryContext); } public void update(EventBean[] newData, EventBean[] oldData) { // handle remove stream: post oldData to all views if (oldData != null && oldData.length != 0) { try { ignoreViewIRStream = true; for (int i = 0; i < views.length; i++) { views[i].update(newData, oldData); } } finally { ignoreViewIRStream = false; } } if (newData != null) { // post to all non-batch views first to let them decide the remove stream, if any try { captureIRNonBatch = true; for (int i = 0; i < views.length; i++) { if (i != batchViewIndex) { views[i].update(newData, oldData); } } } finally { captureIRNonBatch = false; } // if there is any data removed from non-batch views, remove from all views // collect removed events removedEvents.clear(); for (int i = 0; i < views.length; i++) { if (oldEventsPerView[i] != null) { for (int j = 0; j < views.length; j++) { if (i == j) { continue; } views[j].update(null, oldEventsPerView[i]); for (int k = 0; k < oldEventsPerView[i].length; k++) { removedEvents.add(oldEventsPerView[i][k]); } } oldEventsPerView[i] = null; } } // post only new events to the batch view that have not been removed EventBean[] newDataNonRemoved; if (hasAsymetric) { newDataNonRemoved = EventBeanUtility.getNewDataNonRemoved(newData, removedEvents, newEventsPerView); } else { newDataNonRemoved = EventBeanUtility.getNewDataNonRemoved(newData, removedEvents); } if (newDataNonRemoved != null) { views[batchViewIndex].update(newDataNonRemoved, null); } } } public EventType getEventType() { return eventType; } public Iterator<EventBean> iterator() { return views[batchViewIndex].iterator(); } public void newData(int streamId, EventBean[] newEvents, EventBean[] oldEvents) { if (ignoreViewIRStream) { return; } if (captureIRNonBatch) { oldEventsPerView[streamId] = oldEvents; if (hasAsymetric) { newEventsPerView[streamId] = newEvents; } return; } // handle case where irstream originates from view, i.e. timer-based if (streamId == batchViewIndex) { updateChildren(newEvents, oldEvents); if (newEvents != null) { try { ignoreViewIRStream = true; for (int i = 0; i < views.length; i++) { if (i != streamId) { views[i].update(null, newEvents); } } } finally { ignoreViewIRStream = false; } } } // post remove stream to all other views else { if (oldEvents != null) { try { ignoreViewIRStream = true; for (int i = 0; i < views.length; i++) { if (i != streamId) { views[i].update(null, oldEvents); } } } finally { ignoreViewIRStream = false; } } } } @Override public void stopView() { for (View view : views) { if (view instanceof StoppableView) { ((StoppableView) view).stopView(); } } } }