/************************************************************************************** * 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.service; import com.espertech.esper.client.*; import com.espertech.esper.client.context.ContextPartitionSelector; import com.espertech.esper.collection.SafeIteratorImpl; import com.espertech.esper.collection.SingleEventIterator; import com.espertech.esper.dispatch.DispatchService; import com.espertech.esper.timer.TimeSourceService; import com.espertech.esper.view.Viewable; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import java.lang.annotation.Annotation; import java.util.ArrayList; import java.util.Iterator; /** * Statement implementation for EPL statements. */ public class EPStatementImpl implements EPStatementSPI { private static Log log = LogFactory.getLog(EPStatementImpl.class); private final EPStatementListenerSet statementListenerSet; private final String statementId; private final String statementName; private final String expressionText; private final String expressionNoAnnotations; private final boolean nameProvided; private boolean isPattern; private UpdateDispatchViewBase dispatchChildView; private StatementLifecycleSvc statementLifecycleSvc; private long timeLastStateChange; private Viewable parentView; private EPStatementState currentState; private EventType eventType; private StatementMetadata statementMetadata; private Object userObject; private StatementContext statementContext; private String serviceIsolated; /** * Ctor. * @param statementId is a unique ID assigned by the engine for the statement * @param statementName is the statement name assigned during creation, or the statement id if none was assigned * @param expressionText is the EPL and/or pattern expression * @param isPattern is true to indicate this is a pure pattern expression * @param dispatchService for dispatching events to listeners to the statement * @param statementLifecycleSvc handles lifecycle transitions for the statement * @param isBlockingDispatch is true if the dispatch to listeners should block to preserve event generation order * @param isSpinBlockingDispatch true to use spin locks blocking to deliver results, as locks are usually uncontended * @param msecBlockingTimeout is the max number of milliseconds of block time * @param timeLastStateChange the timestamp the statement was created and started * @param timeSourceService time source provider * @param statementMetadata statement metadata * @param userObject the application define user object associated to each statement, if supplied * @param annotations annotations associated to statement * @param statementContext the statement service context * @param expressionNoAnnotations expression text witout annotations * @param isFailed indicator to start in failed state * @param nameProvided true to indicate a statement name has been provided and is not a system-generated name */ public EPStatementImpl(String statementId, String statementName, String expressionText, String expressionNoAnnotations, boolean isPattern, DispatchService dispatchService, StatementLifecycleSvc statementLifecycleSvc, long timeLastStateChange, boolean isBlockingDispatch, boolean isSpinBlockingDispatch, long msecBlockingTimeout, TimeSourceService timeSourceService, StatementMetadata statementMetadata, Object userObject, Annotation[] annotations, StatementContext statementContext, boolean isFailed, boolean nameProvided) { this.isPattern = isPattern; this.statementId = statementId; this.statementName = statementName; this.expressionText = expressionText; this.expressionNoAnnotations = expressionNoAnnotations; this.statementLifecycleSvc = statementLifecycleSvc; this.statementContext = statementContext; this.nameProvided = nameProvided; statementListenerSet = new EPStatementListenerSet(); if (isBlockingDispatch) { if (isSpinBlockingDispatch) { this.dispatchChildView = new UpdateDispatchViewBlockingSpin(statementContext.getStatementResultService(), dispatchService, msecBlockingTimeout, timeSourceService); } else { this.dispatchChildView = new UpdateDispatchViewBlockingWait(statementContext.getStatementResultService(), dispatchService, msecBlockingTimeout); } } else { this.dispatchChildView = new UpdateDispatchViewNonBlocking(statementContext.getStatementResultService(), dispatchService); } if (!isFailed) { this.currentState = EPStatementState.STOPPED; } else { this.currentState = EPStatementState.FAILED; } this.timeLastStateChange = timeLastStateChange; this.statementMetadata = statementMetadata; this.userObject = userObject; statementContext.getStatementResultService().setUpdateListeners(statementListenerSet); } public String getStatementId() { return statementId; } public void start() { if (statementLifecycleSvc == null) { throw new IllegalStateException("Cannot start statement, statement is in destroyed state"); } statementLifecycleSvc.start(statementId); } public void stop() { if (statementLifecycleSvc == null) { throw new IllegalStateException("Cannot stop statement, statement is in destroyed state"); } statementLifecycleSvc.stop(statementId); // On stop, we give the dispatch view a chance to dispatch final results, if any statementContext.getStatementResultService().dispatchOnStop(); dispatchChildView.clear(); } public void destroy() { if (currentState == EPStatementState.DESTROYED) { throw new IllegalStateException("Statement already destroyed"); } statementLifecycleSvc.destroy(statementId); parentView = null; eventType = null; dispatchChildView = null; statementLifecycleSvc = null; } public EPStatementState getState() { return currentState; } public void setCurrentState(EPStatementState currentState, long timeLastStateChange) { this.currentState = currentState; this.timeLastStateChange = timeLastStateChange; } public void setParentView(Viewable viewable) { if (viewable == null) { if (parentView != null) { parentView.removeView(dispatchChildView); } parentView = null; } else { parentView = viewable; parentView.addView(dispatchChildView); eventType = parentView.getEventType(); } } public String getText() { return expressionText; } public String getName() { return statementName; } public Iterator<EventBean> iterator(ContextPartitionSelector selector) { if (statementContext.getContextDescriptor() == null) { throw getUnsupportedNonContextIterator(); } if (selector == null) { throw new IllegalArgumentException("No selector provided"); } // Return null if not started statementContext.getVariableService().setLocalVersion(); if (parentView == null) { return null; } return statementContext.getContextDescriptor().iterator(statementContext.getStatementId(), selector); } public SafeIterator<EventBean> safeIterator(ContextPartitionSelector selector) { if (statementContext.getContextDescriptor() == null) { throw getUnsupportedNonContextIterator(); } if (selector == null) { throw new IllegalArgumentException("No selector provided"); } // Return null if not started if (parentView == null) { return null; } statementContext.getVariableService().setLocalVersion(); return statementContext.getContextDescriptor().safeIterator(statementContext.getStatementId(), selector); } public Iterator<EventBean> iterator() { // Return null if not started statementContext.getVariableService().setLocalVersion(); if (parentView == null) { return null; } if (statementContext.getContextDescriptor() != null) { return statementContext.getContextDescriptor().iterator(statementContext.getStatementId()); } if (isPattern) { return new SingleEventIterator(statementContext.getStatementResultService().getLastIterableEvent()); } else { return parentView.iterator(); } } public SafeIterator<EventBean> safeIterator() { // Return null if not started if (parentView == null) { return null; } if (statementContext.getContextDescriptor() != null) { statementContext.getVariableService().setLocalVersion(); return statementContext.getContextDescriptor().safeIterator(statementContext.getStatementId()); } // Set variable version and acquire the lock first statementContext.getDefaultAgentInstanceLock().acquireReadLock(); try { statementContext.getVariableService().setLocalVersion(); // Provide iterator - that iterator MUST be closed else the lock is not released if (isPattern) { return new SafeIteratorImpl<EventBean>(statementContext.getDefaultAgentInstanceLock(), dispatchChildView.iterator()); } else { return new SafeIteratorImpl<EventBean>(statementContext.getDefaultAgentInstanceLock(), parentView.iterator()); } } catch (RuntimeException ex) { statementContext.getDefaultAgentInstanceLock().releaseReadLock(); throw ex; } } public EventType getEventType() { return eventType; } /** * Returns the set of listeners to the statement. * @return statement listeners */ public EPStatementListenerSet getListenerSet() { return statementListenerSet; } public void setListeners(EPStatementListenerSet listenerSet) { statementListenerSet.setListeners(listenerSet); statementContext.getStatementResultService().setUpdateListeners(listenerSet); } /** * Add a listener to the statement. * @param listener to add */ public void addListener(UpdateListener listener) { if (listener == null) { throw new IllegalArgumentException("Null listener reference supplied"); } if (isDestroyed()) { throw new IllegalStateException("Statement is in destroyed state"); } statementListenerSet.addListener(listener); statementContext.getStatementResultService().setUpdateListeners(statementListenerSet); statementLifecycleSvc.dispatchStatementLifecycleEvent( new StatementLifecycleEvent(this, StatementLifecycleEvent.LifecycleEventType.LISTENER_ADD, listener)); } public void addListenerWithReplay(UpdateListener listener) { if (listener == null) { throw new IllegalArgumentException("Null listener reference supplied"); } if (isDestroyed()) { throw new IllegalStateException("Statement is in destroyed state"); } statementContext.getDefaultAgentInstanceLock().acquireReadLock(); try { // Add listener - listener not receiving events from this statement, as the statement is locked statementListenerSet.addListener(listener); statementContext.getStatementResultService().setUpdateListeners(statementListenerSet); statementLifecycleSvc.dispatchStatementLifecycleEvent( new StatementLifecycleEvent(this, StatementLifecycleEvent.LifecycleEventType.LISTENER_ADD, listener)); Iterator<EventBean> it = iterator(); if (it == null) { try { listener.update(null, null); } catch (Throwable t) { String message = "Unexpected exception invoking listener update method for replay on listener class '" + listener.getClass().getSimpleName() + "' : " + t.getClass().getSimpleName() + " : " + t.getMessage(); log.error(message, t); } return; } ArrayList<EventBean> events = new ArrayList<EventBean>(); for (; it.hasNext();) { events.add(it.next()); } if (events.isEmpty()) { try { listener.update(null, null); } catch (Throwable t) { String message = "Unexpected exception invoking listener update method for replay on listener class '" + listener.getClass().getSimpleName() + "' : " + t.getClass().getSimpleName() + " : " + t.getMessage(); log.error(message, t); } } else { EventBean[] iteratorResult = events.toArray(new EventBean[events.size()]); try { listener.update(iteratorResult, null); } catch (Throwable t) { String message = "Unexpected exception invoking listener update method for replay on listener class '" + listener.getClass().getSimpleName() + "' : " + t.getClass().getSimpleName() + " : " + t.getMessage(); log.error(message, t); } } } finally { statementContext.getDefaultAgentInstanceLock().releaseReadLock(); } } /** * Remove a listeners to a statement. * @param listener to remove */ public void removeListener(UpdateListener listener) { if (listener == null) { throw new IllegalArgumentException("Null listener reference supplied"); } statementListenerSet.removeListener(listener); statementContext.getStatementResultService().setUpdateListeners(statementListenerSet); if (statementLifecycleSvc != null) { statementLifecycleSvc.dispatchStatementLifecycleEvent( new StatementLifecycleEvent(this, StatementLifecycleEvent.LifecycleEventType.LISTENER_REMOVE, listener)); } } /** * Remove all listeners to a statement. */ public void removeAllListeners() { statementListenerSet.removeAllListeners(); statementContext.getStatementResultService().setUpdateListeners(statementListenerSet); if (statementLifecycleSvc != null) { statementLifecycleSvc.dispatchStatementLifecycleEvent( new StatementLifecycleEvent(this, StatementLifecycleEvent.LifecycleEventType.LISTENER_REMOVE_ALL)); } } public void addListener(StatementAwareUpdateListener listener) { if (listener == null) { throw new IllegalArgumentException("Null listener reference supplied"); } if (isDestroyed()) { throw new IllegalStateException("Statement is in destroyed state"); } statementListenerSet.addListener(listener); statementContext.getStatementResultService().setUpdateListeners(statementListenerSet); statementLifecycleSvc.dispatchStatementLifecycleEvent( new StatementLifecycleEvent(this, StatementLifecycleEvent.LifecycleEventType.LISTENER_ADD, listener)); } public void removeListener(StatementAwareUpdateListener listener) { if (listener == null) { throw new IllegalArgumentException("Null listener reference supplied"); } statementListenerSet.removeListener(listener); statementContext.getStatementResultService().setUpdateListeners(statementListenerSet); if (statementLifecycleSvc != null) { statementLifecycleSvc.dispatchStatementLifecycleEvent( new StatementLifecycleEvent(this, StatementLifecycleEvent.LifecycleEventType.LISTENER_REMOVE, listener)); } } public Iterator<StatementAwareUpdateListener> getStatementAwareListeners() { return statementListenerSet.getStmtAwareListeners().iterator(); } public Iterator<UpdateListener> getUpdateListeners() { return statementListenerSet.getListeners().iterator(); } public long getTimeLastStateChange() { return timeLastStateChange; } public boolean isStarted() { return currentState == EPStatementState.STARTED; } public boolean isStopped() { return currentState == EPStatementState.STOPPED; } public boolean isDestroyed() { return currentState == EPStatementState.DESTROYED; } public void setSubscriber(Object subscriber) { statementListenerSet.setSubscriber(subscriber); statementContext.getStatementResultService().setUpdateListeners(statementListenerSet); } public Object getSubscriber() { return statementListenerSet.getSubscriber(); } public boolean isPattern() { return isPattern; } public StatementMetadata getStatementMetadata() { return statementMetadata; } public Object getUserObject() { return userObject; } public Annotation[] getAnnotations() { return statementContext.getAnnotations(); } public StatementContext getStatementContext() { return statementContext; } public String getExpressionNoAnnotations() { return expressionNoAnnotations; } public String getServiceIsolated() { return serviceIsolated; } public void setServiceIsolated(String serviceIsolated) { this.serviceIsolated = serviceIsolated; } public boolean isNameProvided() { return nameProvided; } private UnsupportedOperationException getUnsupportedNonContextIterator() { return new UnsupportedOperationException("Iterator with context selector is only supported for statements under context"); } }