/************************************************************************************** * 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; import com.espertech.esper.client.EventType; import com.espertech.esper.collection.Pair; import com.espertech.esper.core.context.util.AgentInstanceViewFactoryChainContext; import com.espertech.esper.core.service.StatementContext; import com.espertech.esper.epl.spec.StreamSpecOptions; import com.espertech.esper.epl.spec.ViewSpec; import com.espertech.esper.view.internal.IntersectViewFactory; import com.espertech.esper.view.internal.UnionViewFactory; import com.espertech.esper.view.std.GroupByViewFactory; import com.espertech.esper.view.std.GroupByViewFactoryMarker; import com.espertech.esper.view.std.MergeViewFactory; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import java.util.*; /** * Implementation of the view evaluation service business interface. */ public final class ViewServiceImpl implements ViewService { /** * Ctor. */ public ViewServiceImpl() { } public ViewFactoryChain createFactories(int streamNum, EventType parentEventType, List<ViewSpec> viewSpecDefinitions, StreamSpecOptions options, StatementContext context) throws ViewProcessingException { // Clone the view spec list to prevent parameter modification List<ViewSpec> viewSpecList = new ArrayList<ViewSpec>(viewSpecDefinitions); // Inspect views and add merge views if required ViewServiceHelper.addMergeViews(viewSpecList); // Instantiate factories, not making them aware of each other yet List<ViewFactory> viewFactories = ViewServiceHelper.instantiateFactories(streamNum, viewSpecList, context); ViewFactory parentViewFactory = null; List<ViewFactory> attachedViewFactories = new ArrayList<ViewFactory>(); for (int i = 0; i < viewFactories.size(); i++) { ViewFactory factoryToAttach = viewFactories.get(i); try { factoryToAttach.attach(parentEventType, context, parentViewFactory, attachedViewFactories); attachedViewFactories.add(viewFactories.get(i)); parentEventType = factoryToAttach.getEventType(); } catch (ViewParameterException ex) { String text = "Error attaching view to parent view"; if (i == 0) { text = "Error attaching view to event stream"; } throw new ViewProcessingException(text + ": " + ex.getMessage(), ex); } } // obtain count of data windows int dataWindowCount = 0; int firstNonDataWindowIndex = -1; for (int i = 0; i < viewFactories.size(); i++) { ViewFactory factory = viewFactories.get(i); if (factory instanceof DataWindowViewFactory) { dataWindowCount++; continue; } if ((factory instanceof GroupByViewFactoryMarker) || (factory instanceof MergeViewFactory)) { continue; } if (firstNonDataWindowIndex == -1) { firstNonDataWindowIndex = i; } } boolean isAllowMultipleExpiry = context.getConfigSnapshot().getEngineDefaults().getViewResources().isAllowMultipleExpiryPolicies(); boolean isRetainIntersection = options.isRetainIntersection(); boolean isRetainUnion = options.isRetainUnion(); // Set the default to retain-intersection unless allow-multiple-expiry is turned on if ((!isAllowMultipleExpiry) && (!isRetainUnion)) { isRetainIntersection = true; } // handle multiple data windows with retain union. // wrap view factories into the union view factory and handle a group-by, if present if ((isRetainUnion || isRetainIntersection) && dataWindowCount > 1) { viewFactories = getRetainViewFactories(parentEventType, viewFactories, isRetainUnion, context); } return new ViewFactoryChain(parentEventType, viewFactories); } private List<ViewFactory> getRetainViewFactories(EventType parentEventType, List<ViewFactory> viewFactories, boolean isUnion, StatementContext context) throws ViewProcessingException { Set<Integer> groupByFactory = new HashSet<Integer>(); Set<Integer> mergeFactory = new HashSet<Integer>(); List<ViewFactory> derivedValueViews = new ArrayList<ViewFactory>(); List<ViewFactory> dataWindowViews = new ArrayList<ViewFactory>(); for (int i = 0; i < viewFactories.size(); i++) { ViewFactory factory = viewFactories.get(i); if (factory instanceof GroupByViewFactoryMarker) { groupByFactory.add(i); } else if (factory instanceof MergeViewFactory) { mergeFactory.add(i); } else if (factory instanceof DataWindowViewFactory) { dataWindowViews.add(factory); } else { derivedValueViews.add(factory); } } if (groupByFactory.size() > 1) { throw new ViewProcessingException("Multiple groupwin views are not allowed in conjuntion with multiple data windows"); } if ((!groupByFactory.isEmpty()) && (groupByFactory.iterator().next() != 0)) { throw new ViewProcessingException("The groupwin view must occur in the first position in conjuntion with multiple data windows"); } if ((!groupByFactory.isEmpty()) && (mergeFactory.iterator().next() != (viewFactories.size() - 1))) { throw new ViewProcessingException("The merge view cannot be used in conjuntion with multiple data windows"); } GroupByViewFactory groupByViewFactory = null; MergeViewFactory mergeViewFactory = null; if (!groupByFactory.isEmpty()) { groupByViewFactory = (GroupByViewFactory) viewFactories.remove(0); mergeViewFactory = (MergeViewFactory) viewFactories.remove(viewFactories.size() - 1); } ViewFactory retainPolicy; if (isUnion) { UnionViewFactory viewFactory = (UnionViewFactory) context.getViewResolutionService().create("internal", "union"); viewFactory.setParentEventType(parentEventType); viewFactory.setViewFactories(dataWindowViews); retainPolicy = viewFactory; } else { IntersectViewFactory viewFactory = (IntersectViewFactory) context.getViewResolutionService().create("internal", "intersect"); viewFactory.setParentEventType(parentEventType); viewFactory.setViewFactories(dataWindowViews); retainPolicy = viewFactory; } List<ViewFactory> nonRetainViewFactories = new ArrayList<ViewFactory>(); nonRetainViewFactories.add(retainPolicy); if (groupByViewFactory != null) { nonRetainViewFactories.add(0, groupByViewFactory); nonRetainViewFactories.addAll(derivedValueViews); nonRetainViewFactories.add(mergeViewFactory); } else { nonRetainViewFactories.addAll(derivedValueViews); } return nonRetainViewFactories; } public ViewServiceCreateResult createViews(Viewable eventStreamViewable, List<ViewFactory> viewFactories, AgentInstanceViewFactoryChainContext viewFactoryChainContext, boolean hasPreviousNode) { // Attempt to find existing views under the stream that match specs. // The viewSpecList may have been changed by this method. Pair<Viewable, List<View>> resultPair; if (hasPreviousNode) { resultPair = new Pair<Viewable, List<View>>(eventStreamViewable, Collections.<View>emptyList()); } else { resultPair = ViewServiceHelper.matchExistingViews(eventStreamViewable, viewFactories); } Viewable parentViewable = resultPair.getFirst(); if (viewFactories.isEmpty()) { if (log.isDebugEnabled()) { log.debug(".createView No new views created, dumping stream ... " + eventStreamViewable); ViewSupport.dumpChildViews("EventStream ", eventStreamViewable); } return new ViewServiceCreateResult(parentViewable, parentViewable); // we know its a view here since the factory list is empty } // Instantiate remaining chain of views from the remaining factories which didn't match to existing views. List<View> views = ViewServiceHelper.instantiateChain(parentViewable, viewFactories, viewFactoryChainContext); // Initialize any views that need initializing after the chain is complete for (View view : views) { if (view instanceof InitializableView) { InitializableView initView = (InitializableView) view; initView.initialize(); } } if (log.isDebugEnabled()) { log.debug(".createView New views created for stream, all views ... " + eventStreamViewable); ViewSupport.dumpChildViews("EventStream ", eventStreamViewable); } return new ViewServiceCreateResult(views.get(views.size() - 1), views.get(0)); } public void remove(EventStream eventStream, Viewable viewToRemove) { // If the viewToRemove to remove has child viewToRemove, don't disconnect - the child viewToRemove(s) need this if (viewToRemove.hasViews()) { return; } if (log.isDebugEnabled()) { log.debug(".remove Views before the remove of view " + viewToRemove + ", for event stream " + eventStream); ViewSupport.dumpChildViews("EventStream ", eventStream); } // Remove views in chain leaving only non-empty parent views to the child view to be removed ViewServiceHelper.removeChainLeafView(eventStream, viewToRemove); if (log.isDebugEnabled()) { log.debug(".remove Views after the remove, for event stream " + eventStream); ViewSupport.dumpChildViews("EventStream ", eventStream); } } private static final Log log = LogFactory.getLog(ViewServiceImpl.class); }