/*
***************************************************************************************
* 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.regression.nwtable;
import com.espertech.esper.client.ConfigurationPlugInAggregationMultiFunction;
import com.espertech.esper.client.EPServiceProvider;
import com.espertech.esper.client.EPServiceProviderManager;
import com.espertech.esper.client.EventBean;
import com.espertech.esper.client.hook.AggregationFunctionFactory;
import com.espertech.esper.client.scopetest.SupportUpdateListener;
import com.espertech.esper.epl.agg.access.AggregationAccessor;
import com.espertech.esper.epl.agg.access.AggregationAgent;
import com.espertech.esper.epl.agg.access.AggregationState;
import com.espertech.esper.epl.agg.access.AggregationStateKey;
import com.espertech.esper.epl.agg.aggregator.AggregationMethod;
import com.espertech.esper.epl.agg.service.AggregationValidationContext;
import com.espertech.esper.epl.expression.core.ExprEvaluator;
import com.espertech.esper.epl.expression.core.ExprEvaluatorContext;
import com.espertech.esper.epl.rettype.EPType;
import com.espertech.esper.epl.rettype.EPTypeHelper;
import com.espertech.esper.metrics.instrumentation.InstrumentationHelper;
import com.espertech.esper.plugin.*;
import com.espertech.esper.supportregression.bean.SupportBean;
import com.espertech.esper.supportregression.bean.SupportBean_S0;
import com.espertech.esper.supportregression.bean.SupportBean_S1;
import com.espertech.esper.supportregression.client.SupportConfigFactory;
import junit.framework.TestCase;
import java.io.StringWriter;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
public class TestTablePlugInAggregation extends TestCase {
private EPServiceProvider epService;
private SupportUpdateListener listener;
public void setUp() {
epService = EPServiceProviderManager.getDefaultProvider(SupportConfigFactory.getConfiguration());
epService.initialize();
for (Class clazz : new Class[] {SupportBean.class, SupportBean_S0.class, SupportBean_S1.class}) {
epService.getEPAdministrator().getConfiguration().addEventType(clazz);
}
listener = new SupportUpdateListener();
if (InstrumentationHelper.ENABLED) { InstrumentationHelper.startTest(epService, this.getClass(), getName());}
}
public void tearDown() {
if (InstrumentationHelper.ENABLED) { InstrumentationHelper.endTest();}
listener = null;
}
// CSV-building over a limited set of values.
//
// Use aggregation method single-value when the aggregation has a natural current value
// that can be obtained without asking it a question.
public void testPlugInAggMethod_CSVLast3Strings() {
epService.getEPAdministrator().getConfiguration().addPlugInAggregationFunctionFactory("csvWords", SimpleWordCSVFactory.class.getName());
epService.getEPAdministrator().createEPL("create table varagg (csv csvWords())");
epService.getEPAdministrator().createEPL("select varagg.csv as c0 from SupportBean_S0").addListener(listener);
epService.getEPAdministrator().createEPL("into table varagg select csvWords(theString) as csv from SupportBean#length(3)");
sendWordAssert("the", "the");
sendWordAssert("fox", "the,fox");
sendWordAssert("jumps", "the,fox,jumps");
sendWordAssert("over", "fox,jumps,over");
}
// Word counting using a reference-counting-map (similar: count-min-sketch approximation, this one is more limited)
//
// Use aggregation access multi-value when the aggregation must be asked a specific question to return a useful value.
public void testPlugInAccess_RefCountedMap() {
ConfigurationPlugInAggregationMultiFunction config = new ConfigurationPlugInAggregationMultiFunction(
"referenceCountedMap,referenceCountLookup".split(","), ReferenceCountedMapMultiValueFactory.class.getName());
epService.getEPAdministrator().getConfiguration().addPlugInAggregationMultiFunction(config);
epService.getEPAdministrator().createEPL("create table varagg (wordCount referenceCountedMap(string))");
epService.getEPAdministrator().createEPL("into table varagg select referenceCountedMap(theString) as wordCount from SupportBean#length(3)");
epService.getEPAdministrator().createEPL("select varagg.wordCount.referenceCountLookup(p00) as c0 from SupportBean_S0").addListener(listener);
String words = "the,house,is,green";
sendWordAssert("the", words, new Integer[]{1, null, null, null});
sendWordAssert("house", words, new Integer[]{1, 1, null, null});
sendWordAssert("the", words, new Integer[]{2, 1, null, null});
sendWordAssert("green", words, new Integer[]{1, 1, null, 1});
sendWordAssert("is", words, new Integer[]{1, null, 1, 1});
}
private void sendWordAssert(String word, String expected) {
epService.getEPRuntime().sendEvent(new SupportBean(word, 0));
epService.getEPRuntime().sendEvent(new SupportBean_S0(0));
assertEquals(expected, listener.assertOneGetNewAndReset().get("c0"));
}
private void sendWordAssert(String word, String wordCSV, Integer[] counts) {
epService.getEPRuntime().sendEvent(new SupportBean(word, 0));
String[] words = wordCSV.split(",");
for (int i = 0; i < words.length; i++) {
epService.getEPRuntime().sendEvent(new SupportBean_S0(0, words[i]));
Integer count = (Integer) listener.assertOneGetNewAndReset().get("c0");
assertEquals("failed for word '" + words[i] + "'", counts[i], count);
}
}
public static class SimpleWordCSVFactory implements AggregationFunctionFactory {
public void setFunctionName(String functionName) {
}
public void validate(AggregationValidationContext validationContext) {
}
public AggregationMethod newAggregator() {
return new SimpleWordCSVMethod();
}
public Class getValueType() {
return String.class;
}
}
public static class SimpleWordCSVMethod implements AggregationMethod {
private Map<String, Integer> countPerWord = new LinkedHashMap<String, Integer>();
public void enter(Object value) {
String word = (String) value;
Integer count = countPerWord.get(word);
if (count == null) {
countPerWord.put(word, 1);
}
else {
countPerWord.put(word, count + 1);
}
}
public void leave(Object value) {
String word = (String) value;
Integer count = countPerWord.get(word);
if (count == null) {
countPerWord.put(word, 1);
}
else if (count == 1) {
countPerWord.remove(word);
}
else {
countPerWord.put(word, count - 1);
}
}
public Object getValue() {
StringWriter writer = new StringWriter();
String delimiter = "";
for (Map.Entry<String, Integer> entry : countPerWord.entrySet()) {
writer.append(delimiter);
delimiter = ",";
writer.append(entry.getKey());
}
return writer.toString();
}
public void clear() {
countPerWord.clear();
}
}
public static class ReferenceCountedMapMultiValueFactory implements PlugInAggregationMultiFunctionFactory {
private final static AggregationStateKey sharedStateKey = new AggregationStateKey() {};
public void addAggregationFunction(PlugInAggregationMultiFunctionDeclarationContext declarationContext) {
}
public PlugInAggregationMultiFunctionHandler validateGetHandler(PlugInAggregationMultiFunctionValidationContext validationContext) {
if (validationContext.getFunctionName().equals("referenceCountedMap")) {
return new ReferenceCountedMapFunctionHandler(sharedStateKey);
}
if (validationContext.getFunctionName().equals("referenceCountLookup")) {
ExprEvaluator eval = validationContext.getParameterExpressions()[0].getExprEvaluator();
return new ReferenceCountLookupFunctionHandler(sharedStateKey, eval);
}
throw new IllegalArgumentException("Unexpected function name '" + validationContext.getFunctionName());
}
}
public static class RefCountedMapUpdateAgent implements AggregationAgent {
private final ExprEvaluator evaluator;
public RefCountedMapUpdateAgent(ExprEvaluator evaluator) {
this.evaluator = evaluator;
}
public void applyEnter(EventBean[] eventsPerStream, ExprEvaluatorContext exprEvaluatorContext, AggregationState aggregationState) {
Object value = evaluator.evaluate(eventsPerStream, true, exprEvaluatorContext);
RefCountedMapState themap = (RefCountedMapState) aggregationState;
themap.enter(value);
}
public void applyLeave(EventBean[] eventsPerStream, ExprEvaluatorContext exprEvaluatorContext, AggregationState aggregationState) {
Object value = evaluator.evaluate(eventsPerStream, true, exprEvaluatorContext);
RefCountedMapState themap = (RefCountedMapState) aggregationState;
themap.leave(value);
}
}
public static class ReferenceCountedMapFunctionHandler implements PlugInAggregationMultiFunctionHandler {
private final AggregationStateKey sharedStateKey;
public ReferenceCountedMapFunctionHandler(AggregationStateKey sharedStateKey) {
this.sharedStateKey = sharedStateKey;
}
public AggregationAccessor getAccessor() {
return null;
}
public EPType getReturnType() {
return EPTypeHelper.nullValue();
}
public AggregationStateKey getAggregationStateUniqueKey() {
return sharedStateKey;
}
public PlugInAggregationMultiFunctionStateFactory getStateFactory() {
return new PlugInAggregationMultiFunctionStateFactory() {
public AggregationState makeAggregationState(PlugInAggregationMultiFunctionStateContext stateContext) {
return new RefCountedMapState();
}
};
}
public AggregationAgent getAggregationAgent(PlugInAggregationMultiFunctionAgentContext agentContext) {
return new RefCountedMapUpdateAgent(agentContext.getChildNodes()[0].getExprEvaluator());
}
}
public static class ReferenceCountLookupFunctionHandler implements PlugInAggregationMultiFunctionHandler {
private final AggregationStateKey sharedStateKey;
private final ExprEvaluator exprEvaluator;
public ReferenceCountLookupFunctionHandler(AggregationStateKey sharedStateKey, ExprEvaluator exprEvaluator) {
this.sharedStateKey = sharedStateKey;
this.exprEvaluator = exprEvaluator;
}
public AggregationAccessor getAccessor() {
return new ReferenceCountLookupAccessor(exprEvaluator);
}
public EPType getReturnType() {
return EPTypeHelper.singleValue(Integer.class);
}
public AggregationStateKey getAggregationStateUniqueKey() {
return sharedStateKey;
}
public PlugInAggregationMultiFunctionStateFactory getStateFactory() {
throw new IllegalStateException("Getter does not provide the state");
}
public AggregationAgent getAggregationAgent(PlugInAggregationMultiFunctionAgentContext agentContext) {
return null;
}
}
public static class ReferenceCountLookupAccessor implements AggregationAccessor {
private final ExprEvaluator exprEvaluator;
public ReferenceCountLookupAccessor(ExprEvaluator exprEvaluator) {
this.exprEvaluator = exprEvaluator;
}
public Object getValue(AggregationState state, EventBean[] eventsPerStream, boolean isNewData, ExprEvaluatorContext exprEvaluatorContext) {
RefCountedMapState mystate = (RefCountedMapState) state;
Object lookupKey = exprEvaluator.evaluate(eventsPerStream, isNewData, exprEvaluatorContext);
return mystate.getCountPerReference().get(lookupKey);
}
public Collection<EventBean> getEnumerableEvents(AggregationState state, EventBean[] eventsPerStream, boolean isNewData, ExprEvaluatorContext exprEvaluatorContext) {
return null;
}
public EventBean getEnumerableEvent(AggregationState state, EventBean[] eventsPerStream, boolean isNewData, ExprEvaluatorContext exprEvaluatorContext) {
return null;
}
public Collection<Object> getEnumerableScalar(AggregationState state, EventBean[] eventsPerStream, boolean isNewData, ExprEvaluatorContext exprEvaluatorContext) {
return null;
}
}
public static class RefCountedMapState implements AggregationState {
private final Map<Object, Integer> countPerReference = new LinkedHashMap<Object, Integer>();
public Map<Object, Integer> getCountPerReference() {
return countPerReference;
}
public void enter(Object key) {
Integer count = countPerReference.get(key);
if (count == null) {
countPerReference.put(key, 1);
}
else {
countPerReference.put(key, count + 1);
}
}
public void leave(Object key) {
Integer count = countPerReference.get(key);
if (count != null) {
if (count == 1) {
countPerReference.remove(key);
}
else {
countPerReference.put(key, count - 1);
}
}
}
public void applyEnter(EventBean[] eventsPerStream, ExprEvaluatorContext exprEvaluatorContext) {
// no need to implement, we mutate using enter and leave instead
throw new UnsupportedOperationException("Use enter instead");
}
public void applyLeave(EventBean[] eventsPerStream, ExprEvaluatorContext exprEvaluatorContext) {
// no need to implement, we mutate using enter and leave instead
throw new UnsupportedOperationException("Use leave instead");
}
public void clear() {
countPerReference.clear();
}
}
}