/************************************************************************************** * 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.EventBean; import com.espertech.esper.client.EventType; import com.espertech.esper.client.annotation.AuditEnum; import com.espertech.esper.collection.ArrayEventIterator; import com.espertech.esper.core.context.util.AgentInstanceContext; import com.espertech.esper.core.context.util.EPStatementAgentInstanceHandle; import com.espertech.esper.epl.expression.ExprNodeUtility; import com.espertech.esper.filter.FilterSpecCompiled; import com.espertech.esper.util.CollectionUtil; import com.espertech.esper.view.ViewSupport; import java.lang.annotation.Annotation; import java.util.*; import java.util.concurrent.CopyOnWriteArrayList; /** * This view is hooked into a named window's view chain as the last view and handles dispatching of named window * insert and remove stream results via {@link com.espertech.esper.epl.named.NamedWindowService} to consuming statements. */ public class NamedWindowTailViewInstance extends ViewSupport implements Iterable<EventBean> { private final NamedWindowRootViewInstance rootViewInstance; private final NamedWindowTailView tailView; private final AgentInstanceContext agentInstanceContext; private volatile Map<EPStatementAgentInstanceHandle, List<NamedWindowConsumerView>> consumersInContext; // handles as copy-on-write private volatile long numberOfEvents; public NamedWindowTailViewInstance(NamedWindowRootViewInstance rootViewInstance, NamedWindowTailView tailView, AgentInstanceContext agentInstanceContext) { this.rootViewInstance = rootViewInstance; this.tailView = tailView; this.agentInstanceContext = agentInstanceContext; this.consumersInContext = NamedWindowUtil.createConsumerMap(tailView.isPrioritized()); } public void update(EventBean[] newData, EventBean[] oldData) { // Only old data (remove stream) needs to be removed from indexes (kept by root view), if any if (oldData != null) { rootViewInstance.removeOldData(oldData); numberOfEvents -= oldData.length; } if ((newData != null) && (!tailView.isParentBatchWindow())) { rootViewInstance.addNewData(newData); } if (newData != null) { numberOfEvents += newData.length; } // Post to child views, only if there are listeners or subscribers if (tailView.getStatementResultService().isMakeNatural() || tailView.getStatementResultService().isMakeSynthetic()) { updateChildren(newData, oldData); } if (!consumersInContext.isEmpty() || !tailView.getConsumersNonContext().isEmpty()) { NamedWindowDeltaData delta = new NamedWindowDeltaData(newData, oldData); tailView.getNamedWindowService().addDispatch(delta, consumersInContext); tailView.getNamedWindowService().addDispatch(delta, tailView.getConsumersNonContext()); } } /** * Adds a consuming (selecting) statement to the named window. * @return consumer view */ public NamedWindowConsumerView addConsumer(NamedWindowConsumerDesc consumerDesc, boolean isSubselect) { NamedWindowConsumerCallback consumerCallback = new NamedWindowConsumerCallback() { public Iterator<EventBean> getIterator() { return NamedWindowTailViewInstance.this.iterator(); } public void stopped(NamedWindowConsumerView namedWindowConsumerView) { removeConsumer(namedWindowConsumerView); } }; // Construct consumer view, allow a callback to this view to remove the consumer boolean audit = AuditEnum.STREAM.getAudit(consumerDesc.getAgentInstanceContext().getStatementContext().getAnnotations()) != null; NamedWindowConsumerView consumerView = new NamedWindowConsumerView(ExprNodeUtility.getEvaluators(consumerDesc.getFilterList()), consumerDesc.getOptPropertyEvaluator(), tailView.getEventType(), consumerCallback, consumerDesc.getAgentInstanceContext(), audit); // Keep a list of consumer views per statement to accomodate joins and subqueries List<NamedWindowConsumerView> viewsPerStatements = consumersInContext.get(consumerDesc.getAgentInstanceContext().getEpStatementAgentInstanceHandle()); if (viewsPerStatements == null) { viewsPerStatements = new CopyOnWriteArrayList<NamedWindowConsumerView>(); // avoid concurrent modification as a thread may currently iterate over consumers as its dispatching // without the engine lock Map<EPStatementAgentInstanceHandle, List<NamedWindowConsumerView>> newConsumers = NamedWindowUtil.createConsumerMap(tailView.isPrioritized()); newConsumers.putAll(consumersInContext); newConsumers.put(consumerDesc.getAgentInstanceContext().getEpStatementAgentInstanceHandle(), viewsPerStatements); consumersInContext = newConsumers; } if (isSubselect) { viewsPerStatements.add(0, consumerView); } else { viewsPerStatements.add(consumerView); } return consumerView; } /** * Called by the consumer view to indicate it was stopped or destroyed, such that the * consumer can be deregistered and further dispatches disregard this consumer. * @param namedWindowConsumerView is the consumer representative view */ public void removeConsumer(NamedWindowConsumerView namedWindowConsumerView) { EPStatementAgentInstanceHandle handleRemoved = null; // Find the consumer view for (Map.Entry<EPStatementAgentInstanceHandle, List<NamedWindowConsumerView>> entry : consumersInContext.entrySet()) { boolean foundAndRemoved = entry.getValue().remove(namedWindowConsumerView); // Remove the consumer view if ((foundAndRemoved) && (entry.getValue().size() == 0)) { // Remove the handle if this list is now empty handleRemoved = entry.getKey(); break; } } if (handleRemoved != null) { Map<EPStatementAgentInstanceHandle, List<NamedWindowConsumerView>> newConsumers = NamedWindowUtil.createConsumerMap(tailView.isPrioritized()); newConsumers.putAll(consumersInContext); newConsumers.remove(handleRemoved); consumersInContext = newConsumers; } } public EventType getEventType() { return tailView.getEventType(); } public Iterator<EventBean> iterator() { if (tailView.getRevisionProcessor() != null) { Collection<EventBean> coll = tailView.getRevisionProcessor().getSnapshot(agentInstanceContext.getEpStatementAgentInstanceHandle(), parent); return coll.iterator(); } agentInstanceContext.getEpStatementAgentInstanceHandle().getStatementAgentInstanceLock().acquireReadLock(); try { Iterator<EventBean> it = parent.iterator(); if (!it.hasNext()) { return CollectionUtil.NULL_EVENT_ITERATOR; } ArrayList<EventBean> list = new ArrayList<EventBean>(); while (it.hasNext()) { list.add(it.next()); } return new ArrayEventIterator(list.toArray(new EventBean[list.size()])); } finally { agentInstanceContext.getEpStatementAgentInstanceHandle().getStatementAgentInstanceLock().releaseReadLock(); } } /** * Returns a snapshot of window contents, thread-safely * @param filter filters if any * @return window contents */ public Collection<EventBean> snapshot(FilterSpecCompiled filter, Annotation[] annotations) { if (tailView.getRevisionProcessor() != null) { return tailView.getRevisionProcessor().getSnapshot(agentInstanceContext.getEpStatementAgentInstanceHandle(), parent); } agentInstanceContext.getEpStatementAgentInstanceHandle().getStatementAgentInstanceLock().acquireReadLock(); try { return snapshotNoLock(filter, annotations); } finally { agentInstanceContext.getEpStatementAgentInstanceHandle().getStatementAgentInstanceLock().releaseReadLock(); } } public Collection<EventBean> snapshotNoLock(FilterSpecCompiled filter, Annotation[] annotations) { if (tailView.getRevisionProcessor() != null) { return tailView.getRevisionProcessor().getSnapshot(agentInstanceContext.getEpStatementAgentInstanceHandle(), parent); } Collection<EventBean> indexedResult = rootViewInstance.snapshot(filter, annotations); if (indexedResult != null) { return indexedResult; } Iterator<EventBean> it = parent.iterator(); if (!it.hasNext()) { return Collections.EMPTY_LIST; } ArrayDeque<EventBean> list = new ArrayDeque<EventBean>(); while (it.hasNext()) { list.add(it.next()); } return list; } public AgentInstanceContext getAgentInstanceContext() { return agentInstanceContext; } /** * Destroy the view. */ public void destroy() { consumersInContext = NamedWindowUtil.createConsumerMap(tailView.isPrioritized()); } /** * Returns the number of events held. * @return number of events */ public long getNumberOfEvents() { return numberOfEvents; } public NamedWindowTailView getTailView() { return tailView; } }