/************************************************************************************** * 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.stream; import com.espertech.esper.client.EventBean; import com.espertech.esper.client.EventType; import com.espertech.esper.collection.Pair; import com.espertech.esper.collection.RefCountedMap; import com.espertech.esper.core.context.util.EPStatementAgentInstanceHandle; import com.espertech.esper.core.service.EPStatementHandleCallback; import com.espertech.esper.core.service.StatementAgentInstanceLock; import com.espertech.esper.epl.expression.ExprEvaluatorContext; import com.espertech.esper.filter.FilterHandleCallback; import com.espertech.esper.filter.FilterService; import com.espertech.esper.filter.FilterSpecCompiled; import com.espertech.esper.filter.FilterValueSet; import com.espertech.esper.view.EventStream; import com.espertech.esper.view.ZeroDepthStream; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import java.lang.annotation.Annotation; import java.util.Collection; import java.util.IdentityHashMap; /** * Service implementation to reuse or not reuse event streams and existing filters depending on * the type of statement. * <p> * For non-join statements, the class manages the reuse of event streams when filters match, and thus * when an event stream is reused such can be the views under the stream. For joins however, this can lead to * problems in multithread-safety since the statement resource lock would then have to be multiple locks, * i.e. the reused statement's resource lock and the join statement's own lock, at a minimum. * <p> * For join statements, always creating a new event stream and * therefore not reusing view resources, for use with joins. * <p> * This can be very effective in that if a client applications creates a large number of very similar * statements in terms of filters and views used then these resources are all re-used * across statements. * <p> * The re-use is multithread-safe in that * (A) statement start/stop is locked against other engine processing * (B) the first statement supplies the lock for shared filters and views, protecting multiple threads * from entering into the same view. * (C) joins statements do not participate in filter and view reuse */ public class StreamFactorySvcImpl implements StreamFactoryService { private static Log log = LogFactory.getLog(StreamFactorySvcImpl.class); // Using identify hash map - ignoring the equals semantics on filter specs // Thus two filter specs objects are always separate entries in the map private final IdentityHashMap<Object, Pair<EventStream, EPStatementHandleCallback>> eventStreamsIdentity; // Using a reference-counted map for non-join statements private final RefCountedMap<FilterSpecCompiled, Pair<EventStream, EPStatementHandleCallback>> eventStreamsRefCounted; private final String engineURI; private final boolean isReuseViews; /** * Ctor. * @param isReuseViews indicator on whether stream and view resources are to be reused between statements */ public StreamFactorySvcImpl(String engineURI, boolean isReuseViews) { this.engineURI = engineURI; this.eventStreamsRefCounted = new RefCountedMap<FilterSpecCompiled, Pair<EventStream, EPStatementHandleCallback>>(); this.eventStreamsIdentity = new IdentityHashMap<Object, Pair<EventStream, EPStatementHandleCallback>>(); this.isReuseViews = isReuseViews; } public void destroy() { eventStreamsRefCounted.clear(); eventStreamsIdentity.clear(); } /** * See the method of the same name in {@link com.espertech.esper.view.stream.StreamFactoryService}. Always attempts to reuse an existing event stream. * May thus return a new event stream or an existing event stream depending on whether filter criteria match. * * @param filterSpec is the filter definition * @param epStatementAgentInstanceHandle is the statement resource lock * @return newly createdStatement event stream, not reusing existing instances */ public Pair<EventStream, StatementAgentInstanceLock> createStream(final String statementId, final FilterSpecCompiled filterSpec, FilterService filterService, EPStatementAgentInstanceHandle epStatementAgentInstanceHandle, boolean isJoin, final boolean isSubSelect, final ExprEvaluatorContext exprEvaluatorContext, boolean isNamedWindowTrigger, boolean filterWithSameTypeSubselect, Annotation[] annotations, boolean stateless ) { if (log.isDebugEnabled()) { log.debug(".createStream hashCode=" + filterSpec.hashCode() + " filter=" + filterSpec); } // Check if a stream for this filter already exists Pair<EventStream, EPStatementHandleCallback> pair; boolean forceNewStream = isJoin || (!isReuseViews) || isSubSelect || isNamedWindowTrigger || filterWithSameTypeSubselect || stateless; if (forceNewStream) { pair = eventStreamsIdentity.get(filterSpec); } else { pair = eventStreamsRefCounted.get(filterSpec); } // If pair exists, either reference count or illegal state if (pair != null) { if (forceNewStream) { throw new IllegalStateException("Filter spec object already found in collection"); } else { log.debug(".createStream filter already found"); eventStreamsRefCounted.reference(filterSpec); // audit proxy EventStream eventStream = EventStreamProxy.getAuditProxy(engineURI, epStatementAgentInstanceHandle.getStatementHandle().getStatementName(), annotations, filterSpec, pair.getFirst()); // We return the lock of the statement first establishing the stream to use that as the new statement's lock return new Pair<EventStream, StatementAgentInstanceLock>(eventStream, pair.getSecond().getAgentInstanceHandle().getStatementAgentInstanceLock()); } } // New event stream EventType resultEventType = filterSpec.getResultEventType(); EventStream zeroDepthStream = new ZeroDepthStream(resultEventType); // audit proxy EventStream inputStream = EventStreamProxy.getAuditProxy(engineURI, epStatementAgentInstanceHandle.getStatementHandle().getStatementName(), annotations, filterSpec, zeroDepthStream); final EventStream eventStream = inputStream; FilterHandleCallback filterCallback; if (filterSpec.getOptionalPropertyEvaluator() != null) { filterCallback = new FilterHandleCallback() { public String getStatementId() { return statementId; } public void matchFound(EventBean theEvent, Collection<FilterHandleCallback> allStmtMatches) { EventBean[] result = filterSpec.getOptionalPropertyEvaluator().getProperty(theEvent, exprEvaluatorContext); if (result == null) { return; } eventStream.insert(result); } public boolean isSubSelect() { return isSubSelect; } }; } else { filterCallback = new FilterHandleCallback() { public String getStatementId() { return statementId; } public void matchFound(EventBean theEvent, Collection<FilterHandleCallback> allStmtMatches) { eventStream.insert(theEvent); } public boolean isSubSelect() { return isSubSelect; } }; } EPStatementHandleCallback handle = new EPStatementHandleCallback(epStatementAgentInstanceHandle, filterCallback); // Store stream for reuse pair = new Pair<EventStream, EPStatementHandleCallback>(eventStream, handle); if (forceNewStream) { eventStreamsIdentity.put(filterSpec, pair); } else { eventStreamsRefCounted.put(filterSpec, pair); } // Activate filter FilterValueSet filterValues = filterSpec.getValueSet(null, exprEvaluatorContext, null); filterService.add(filterValues, handle); return new Pair<EventStream, StatementAgentInstanceLock>(inputStream, null); } /** * See the method of the same name in {@link com.espertech.esper.view.stream.StreamFactoryService}. * @param filterSpec is the filter definition */ public void dropStream(FilterSpecCompiled filterSpec, FilterService filterService, boolean isJoin, boolean isSubSelect, boolean isNamedWindowTrigger, boolean filterWithSameTypeSubselect, boolean stateless) { Pair<EventStream, EPStatementHandleCallback> pair; boolean forceNewStream = isJoin || (!isReuseViews) || isSubSelect || isNamedWindowTrigger || filterWithSameTypeSubselect || stateless; if (forceNewStream) { pair = eventStreamsIdentity.get(filterSpec); if (pair == null) { throw new IllegalStateException("Filter spec object not in collection"); } eventStreamsIdentity.remove(filterSpec); filterService.remove(pair.getSecond()); } else { pair = eventStreamsRefCounted.get(filterSpec); boolean isLast = eventStreamsRefCounted.dereference(filterSpec); if (isLast) { filterService.remove(pair.getSecond()); } } } }