/*
* *************************************************************************************
* 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.dataflow.ops;
import com.espertech.esper.client.EventBean;
import com.espertech.esper.client.EventType;
import com.espertech.esper.client.dataflow.EPDataFlowSignalFinalMarker;
import com.espertech.esper.core.service.StatementContext;
import com.espertech.esper.dataflow.annotations.DataFlowContext;
import com.espertech.esper.dataflow.annotations.DataFlowOpParameter;
import com.espertech.esper.dataflow.annotations.DataFlowOperator;
import com.espertech.esper.dataflow.interfaces.*;
import com.espertech.esper.dataflow.util.GraphTypeDesc;
import com.espertech.esper.epl.expression.*;
import com.espertech.esper.event.EventBeanManufacturer;
import com.espertech.esper.event.EventTypeUtility;
import com.espertech.esper.event.WriteablePropertyDescriptor;
import com.espertech.esper.util.TypeWidener;
import com.espertech.esper.util.TypeWidenerFactory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.util.*;
@DataFlowOperator
public class BeaconSource implements DataFlowSourceOperator {
private static final Log log = LogFactory.getLog(BeaconSource.class);
private final static List<String> PARAMETER_PROPERTIES = Arrays.asList("iterations", "initialDelay", "period");
@DataFlowContext
private EPDataFlowEmitter graphContext;
@DataFlowOpParameter
private long iterations;
@DataFlowOpParameter
private double initialDelay;
@DataFlowOpParameter
private double interval;
private Map<String, Object> allProperties = new LinkedHashMap<String, Object>();
private long initialDelayMSec;
private long periodDelayMSec;
private long lastSendTime;
private long iterationNumber;
private boolean produceEventBean;
private ExprEvaluator[] evaluators;
private EventBeanManufacturer manufacturer;
@DataFlowOpParameter(all=true)
public void setProperty(String name, Object value) {
allProperties.put(name, value);
}
public DataFlowOpInitializeResult initialize(DataFlowOpInitializateContext context) throws Exception {
initialDelayMSec = (long) (initialDelay * 1000);
periodDelayMSec = (long) (interval * 1000);
if (context.getOutputPorts().size() != 1) {
throw new IllegalArgumentException("BeaconSource operator requires one output stream but produces " + context.getOutputPorts().size() + " streams");
}
// Check if a type is declared
DataFlowOpOutputPort port = context.getOutputPorts().get(0);
if (port.getOptionalDeclaredType() != null && port.getOptionalDeclaredType().getEventType() != null) {
EventType outputEventType = port.getOptionalDeclaredType().getEventType();
produceEventBean = port.getOptionalDeclaredType() != null && !port.getOptionalDeclaredType().isUnderlying();
// compile properties to populate
Set<String> props = allProperties.keySet();
props.removeAll(PARAMETER_PROPERTIES);
WriteablePropertyDescriptor[] writables = setupProperties(props.toArray(new String[props.size()]), outputEventType, context.getStatementContext());
manufacturer = context.getServicesContext().getEventAdapterService().getManufacturer(outputEventType, writables, context.getServicesContext().getEngineImportService());
int index = 0;
evaluators = new ExprEvaluator[outputEventType.getPropertyDescriptors().length];
for (WriteablePropertyDescriptor writeable : writables) {
ExprNode exprNode = (ExprNode) allProperties.get(writeable.getPropertyName());
if (exprNode == null) {
evaluators[index] = new ExprEvaluator() {
public Object evaluate(EventBean[] eventsPerStream, boolean isNewData, ExprEvaluatorContext context) {
return null;
}
public Class getType() {
return null;
}
public Map<String, Object> getEventType() throws ExprValidationException {
return null;
}
};
}
else {
ExprNode validated = ExprNodeUtility.validateSimpleGetSubtree(exprNode, context.getStatementContext(), null);
final ExprEvaluator exprEvaluator = validated.getExprEvaluator();
final TypeWidener widener = TypeWidenerFactory.getCheckPropertyAssignType(validated.toExpressionString(), exprEvaluator.getType(),
writeable.getType(), writeable.getPropertyName());
if (widener != null) {
evaluators[index] = new ExprEvaluator() {
public Object evaluate(EventBean[] eventsPerStream, boolean isNewData, ExprEvaluatorContext context) {
Object value = exprEvaluator.evaluate(eventsPerStream, isNewData, context);
return widener.widen(value);
}
public Class getType() {
return null;
}
public Map<String, Object> getEventType() throws ExprValidationException {
return null;
}
};
}
else {
evaluators[index] = exprEvaluator;
}
}
index++;
}
return null; // no changing types
}
// No type has been declared, we can create one
String anonymousTypeName = context.getDataflowName() + "-beacon";
Map<String, Object> types = new LinkedHashMap<String, Object>();
Set<String> props = allProperties.keySet();
props.removeAll(PARAMETER_PROPERTIES);
int count = 0;
evaluators = new ExprEvaluator[props.size()];
for (String propertyName : props) {
ExprNode exprNode = (ExprNode) allProperties.get(propertyName);
ExprNode validated = ExprNodeUtility.validateSimpleGetSubtree(exprNode, context.getStatementContext(), null);
final Object value = validated.getExprEvaluator().evaluate(null, true, context.getAgentInstanceContext());
if (value == null) {
types.put(propertyName, null);
}
else {
types.put(propertyName, value.getClass());
}
evaluators[count] = new ExprEvaluator() {
public Object evaluate(EventBean[] eventsPerStream, boolean isNewData, ExprEvaluatorContext context) {
return value;
}
public Class getType() {
return null;
}
public Map<String, Object> getEventType() throws ExprValidationException {
return null;
}
};
count++;
}
EventType type = context.getServicesContext().getEventAdapterService().createAnonymousObjectArrayType(anonymousTypeName, types);
return new DataFlowOpInitializeResult(new GraphTypeDesc[] {new GraphTypeDesc(false, true, type)});
}
public void next() {
if (iterationNumber == 0 && initialDelayMSec > 0) {
try {
Thread.sleep(initialDelayMSec, 0);
}
catch (InterruptedException e) {
graphContext.submitSignal(new EPDataFlowSignalFinalMarker() {});
}
}
if (iterationNumber > 0 && periodDelayMSec > 0) {
long nsecDelta = lastSendTime - System.nanoTime();
long sleepTime = periodDelayMSec - nsecDelta / 1000000;
if (sleepTime > 0) {
try {
Thread.sleep(sleepTime);
}
catch (InterruptedException e) {
graphContext.submitSignal(new EPDataFlowSignalFinalMarker() {});
}
}
}
if (iterations > 0 && iterationNumber >= iterations) {
graphContext.submitSignal(new EPDataFlowSignalFinalMarker() {});
}
else {
iterationNumber++;
if (evaluators != null) {
Object[] row = new Object[evaluators.length];
for (int i = 0; i < row.length; i++) {
row[i] = evaluators[i].evaluate(null, true, null);
}
if (log.isDebugEnabled()) {
log.debug("BeaconSource submitting row " + Arrays.toString(row));
}
Object outputEvent = row;
if (manufacturer != null) {
if (!produceEventBean) {
outputEvent = manufacturer.makeUnderlying(row);
}
else {
outputEvent = manufacturer.make(row);
}
}
graphContext.submit(outputEvent);
}
else {
if (log.isDebugEnabled()) {
log.debug("BeaconSource submitting empty row");
}
graphContext.submit(new Object[0]);
}
if (interval > 0) {
lastSendTime = System.nanoTime();
}
}
}
public void open(DataFlowOpOpenContext openContext) {
// no action
}
public void close(DataFlowOpCloseContext openContext) {
// no action
}
private static WriteablePropertyDescriptor[] setupProperties(String[] propertyNamesOffered, EventType outputEventType, StatementContext statementContext)
throws ExprValidationException
{
Set<WriteablePropertyDescriptor> writeables = statementContext.getEventAdapterService().getWriteableProperties(outputEventType);
List<WriteablePropertyDescriptor> writablesList = new ArrayList<WriteablePropertyDescriptor>();
for (int i = 0; i < propertyNamesOffered.length; i++) {
String propertyName = propertyNamesOffered[i];
WriteablePropertyDescriptor writable = EventTypeUtility.findWritable(propertyName, writeables);
if (writable == null) {
throw new ExprValidationException("Failed to find writable property '" + propertyName + "' for event type '" + outputEventType.getName() + "'");
}
writablesList.add(writable);
}
return writablesList.toArray(new WriteablePropertyDescriptor[writablesList.size()]);
}
}