/*
***************************************************************************************
* Copyright (C) 2006 EsperTech, Inc. All rights reserved. *
* http://www.espertech.com/esper *
* 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.SafeIteratorWTableImpl;
import com.espertech.esper.collection.UnsafeIteratorWTableImpl;
import com.espertech.esper.dispatch.DispatchService;
import com.espertech.esper.timer.TimeSourceService;
import com.espertech.esper.view.Viewable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
/**
* Statement implementation for EPL statements.
*/
public class EPStatementImpl implements EPStatementSPI {
private static Logger log = LoggerFactory.getLogger(EPStatementImpl.class);
private final EPStatementListenerSet statementListenerSet;
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;
protected StatementContext statementContext;
private String serviceIsolated;
/**
* Ctor.
*
* @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 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 expressionNoAnnotations,
boolean isPattern,
DispatchService dispatchService,
StatementLifecycleSvc statementLifecycleSvc,
long timeLastStateChange,
boolean isBlockingDispatch,
boolean isSpinBlockingDispatch,
long msecBlockingTimeout,
TimeSourceService timeSourceService,
StatementMetadata statementMetadata,
Object userObject,
StatementContext statementContext,
boolean isFailed,
boolean nameProvided) {
this.isPattern = isPattern;
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, false);
}
public int getStatementId() {
return statementContext.getStatementId();
}
public void start() {
if (statementLifecycleSvc == null) {
throw new IllegalStateException("Cannot start statement, statement is in destroyed state");
}
statementLifecycleSvc.start(statementContext.getStatementId());
}
public void stop() {
if (statementLifecycleSvc == null) {
throw new IllegalStateException("Cannot stop statement, statement is in destroyed state");
}
statementLifecycleSvc.stop(statementContext.getStatementId());
// 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(statementContext.getStatementId());
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 Viewable getParentView() {
return parentView;
}
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 statementContext.getExpression();
}
public String getName() {
return statementContext.getStatementName();
}
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;
}
Iterator<EventBean> theIterator;
if (statementContext.getContextDescriptor() != null) {
theIterator = statementContext.getContextDescriptor().iterator(statementContext.getStatementId());
} else {
theIterator = parentView.iterator();
}
if (statementContext.getEpStatementHandle().isHasTableAccess()) {
return new UnsafeIteratorWTableImpl(statementContext.getTableExprEvaluatorContext(), theIterator);
}
return theIterator;
}
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 (statementContext.getEpStatementHandle().isHasTableAccess()) {
return new SafeIteratorWTableImpl<EventBean>(statementContext.getDefaultAgentInstanceLock(), parentView.iterator(), statementContext.getTableExprEvaluatorContext());
}
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, boolean isRecovery) {
statementListenerSet.setListeners(listenerSet);
statementContext.getStatementResultService().setUpdateListeners(listenerSet, isRecovery);
}
/**
* 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, false);
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, false);
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 {
if (statementContext.getEpStatementHandle().isHasTableAccess()) {
statementContext.getTableExprEvaluatorContext().releaseAcquiredLocks();
}
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, false);
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, false);
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, false);
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, false);
if (statementLifecycleSvc != null) {
statementLifecycleSvc.dispatchStatementLifecycleEvent(
new StatementLifecycleEvent(this, StatementLifecycleEvent.LifecycleEventType.LISTENER_REMOVE, listener));
}
}
public Iterator<StatementAwareUpdateListener> getStatementAwareListeners() {
return Arrays.asList(statementListenerSet.getStmtAwareListeners()).iterator();
}
public Iterator<UpdateListener> getUpdateListeners() {
return Arrays.asList(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) {
setSubscriber(subscriber, null);
}
public void setSubscriber(Object subscriber, String methodName) {
statementListenerSet.setSubscriber(subscriber, methodName);
statementContext.getStatementResultService().setUpdateListeners(statementListenerSet, false);
}
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;
}
public UpdateDispatchViewBase getDispatchChildView() {
return dispatchChildView;
}
private UnsupportedOperationException getUnsupportedNonContextIterator() {
return new UnsupportedOperationException("Iterator with context selector is only supported for statements under context");
}
}