/* * ************************************************************************************* * 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.regression.dataflow; import com.espertech.esper.client.EPServiceProvider; import com.espertech.esper.client.EPServiceProviderManager; import com.espertech.esper.client.EPStatement; import com.espertech.esper.client.EPStatementException; import com.espertech.esper.client.scopetest.EPAssertionUtil; import com.espertech.esper.dataflow.annotations.DataFlowOpParameter; import com.espertech.esper.dataflow.annotations.DataFlowOperator; import com.espertech.esper.support.client.SupportConfigFactory; import junit.framework.TestCase; import java.util.ArrayList; import java.util.List; import java.util.Map; // Further relevant tests in JSONUtil/PopulateUtil public class TestCustomProperties extends TestCase { private EPServiceProvider engine; public void setUp() { engine = EPServiceProviderManager.getDefaultProvider(SupportConfigFactory.getConfiguration()); engine.initialize(); } /** * - GraphSource always has output ports: * (A) Either as declared through @OutputTypes annotation * (B) Or as assigned via stream (GraphSource -> OutStream<Type>) * * - Operator properties are explicit: * (A) There is a public setter method * (B) Or the @GraphOpProperty annotation is declared on a field or setter method (optionally can provide a name) * (C) Or the @GraphOpProperty annotation is declared on a catch-all method * * - Graph op property types * (A) Scalar type * (B) or ExprNode * (C) or Json for nested objects and array * (D) or EPL select * * - Graph ops communicate the underlying events * - should EventBean be need for event evaluation, the EventBean instance is pooled/shared locally by the op * - if the event bus should evaluate the event, a new anonymous event gets created with the desired type attached dynamically * * - Exception handlings * - Validation of syntax is performed during "createEPL" * - Resolution of operators and types is performed during "instantiate" * - Runtime exception handling depends on how the data flow gets started and always uses an exception handler (another subject therefore) */ public void testInvalid() { String epl; epl = "create dataflow MyGraph ABC { field: { a: a}}"; tryInvalid(epl, "Incorrect syntax near 'a' at line 1 column 42 [create dataflow MyGraph ABC { field: { a: a}}]"); epl = "create dataflow MyGraph ABC { field: { a:1; b:2 }}"; tryInvalid(epl, "Incorrect syntax near ';' expecting a right curly bracket '}' but found a semicolon ';' at line 1 column 42 [create dataflow MyGraph ABC { field: { a:1; b:2 }}]"); } private void tryInvalid(String epl, String message) { try { engine.getEPAdministrator().createEPL(epl); fail(); } catch (EPStatementException ex) { assertEquals(message, ex.getMessage()); } } public void testCustomProps() { engine.getEPAdministrator().getConfiguration().addImport(MyOperatorOne.class); engine.getEPAdministrator().getConfiguration().addImport(MyOperatorTwo.class); // test simple properties MyOperatorOne.getOperators().clear(); EPStatement stmtGraph = engine.getEPAdministrator().createEPL("create dataflow MyGraph " + MyOperatorOne.class.getSimpleName() + " {" + " theString = 'a'," + " theInt: 1," + " theBool: true," + " theLongOne: 1L," + " theLongTwo: 2," + " theLongThree: null," + " theDoubleOne: 1d," + " theDoubleTwo: 2," + " theFloatOne: 1f," + " theFloatTwo: 2," + " theStringWithSetter: 'b'," + " theSystemProperty: systemProperties('log4j.configuration')" + "}"); engine.getEPRuntime().getDataFlowRuntime().instantiate("MyGraph"); assertEquals(1, MyOperatorOne.getOperators().size()); MyOperatorOne instanceOne = MyOperatorOne.getOperators().get(0); assertEquals("a", instanceOne.getTheString()); assertEquals(null, instanceOne.getTheNotSetString()); assertEquals(1, instanceOne.getTheInt()); assertEquals(true, instanceOne.isTheBool()); assertEquals(1L, (long) instanceOne.getTheLongOne()); assertEquals(2, instanceOne.getTheLongTwo()); assertEquals(null, instanceOne.getTheLongThree()); assertEquals(1.0, instanceOne.getTheDoubleOne()); assertEquals(2.0, instanceOne.getTheDoubleTwo()); assertEquals(1f, instanceOne.getTheFloatOne()); assertEquals(2f, instanceOne.getTheFloatTwo()); assertEquals(">b<", instanceOne.getTheStringWithSetter()); assertNotNull(instanceOne.getTheSystemProperty()); stmtGraph.destroy(); // test array etc. properties MyOperatorTwo.getOperators().clear(); engine.getEPAdministrator().createEPL("create dataflow MyGraph " + MyOperatorTwo.class.getSimpleName() + " {\n" + " theStringArray: ['a', \"b\"],\n" + " theIntArray: [1, 2, 3],\n" + " theObjectArray: ['a', 1],\n" + " theMap: {\n" + " a : 10,\n" + " b : 'xyz'\n" + " },\n" + " theInnerOp: {\n" + " fieldOne: 'x',\n" + " fieldTwo: 2\n" + " },\n" + " theInnerOpInterface: {\n" + " class: '" + MyOperatorTwoInterfaceImplTwo.class.getName() + "'\n" + " },\n" + // NOTE the last comma here, it's acceptable "}"); engine.getEPRuntime().getDataFlowRuntime().instantiate("MyGraph"); assertEquals(1, MyOperatorTwo.getOperators().size()); MyOperatorTwo instanceTwo = MyOperatorTwo.getOperators().get(0); EPAssertionUtil.assertEqualsExactOrder(new String[] {"a", "b"}, instanceTwo.getTheStringArray()); EPAssertionUtil.assertEqualsExactOrder(new int[]{1, 2, 3}, instanceTwo.getTheIntArray()); EPAssertionUtil.assertEqualsExactOrder(new Object[] {"a", 1}, instanceTwo.getTheObjectArray()); EPAssertionUtil.assertPropsMap(instanceTwo.getTheMap(), "a,b".split(","), new Object[]{10, "xyz"}); assertEquals("x", instanceTwo.getTheInnerOp().fieldOne); assertEquals(2, instanceTwo.getTheInnerOp().fieldTwo); assertTrue(instanceTwo.getTheInnerOpInterface() instanceof MyOperatorTwoInterfaceImplTwo); } @DataFlowOperator public static class MyOperatorOne { private static List<MyOperatorOne> operators = new ArrayList<MyOperatorOne>(); public static List<MyOperatorOne> getOperators() { return operators; } public MyOperatorOne() { operators.add(this); } @DataFlowOpParameter private String theString; @DataFlowOpParameter private String theNotSetString; @DataFlowOpParameter private int theInt; @DataFlowOpParameter private boolean theBool; @DataFlowOpParameter private Long theLongOne; @DataFlowOpParameter private long theLongTwo; @DataFlowOpParameter private Long theLongThree; @DataFlowOpParameter private double theDoubleOne; @DataFlowOpParameter private Double theDoubleTwo; @DataFlowOpParameter private float theFloatOne; @DataFlowOpParameter private Float theFloatTwo; @DataFlowOpParameter private String theSystemProperty; private String theStringWithSetter; public String getTheString() { return theString; } public String getTheNotSetString() { return theNotSetString; } public int getTheInt() { return theInt; } public boolean isTheBool() { return theBool; } public Long getTheLongOne() { return theLongOne; } public long getTheLongTwo() { return theLongTwo; } public Long getTheLongThree() { return theLongThree; } public float getTheFloatOne() { return theFloatOne; } public Float getTheFloatTwo() { return theFloatTwo; } public double getTheDoubleOne() { return theDoubleOne; } public Double getTheDoubleTwo() { return theDoubleTwo; } public String getTheStringWithSetter() { return theStringWithSetter; } public void setTheStringWithSetter(String theStringWithSetter) { this.theStringWithSetter = ">" + theStringWithSetter + "<"; } public String getTheSystemProperty() { return theSystemProperty; } } @DataFlowOperator public static class MyOperatorTwo { private static List<MyOperatorTwo> operators = new ArrayList<MyOperatorTwo>(); public static List<MyOperatorTwo> getOperators() { return operators; } public MyOperatorTwo() { operators.add(this); } private String[] theStringArray; private int[] theIntArray; private Object[] theObjectArray; private Map<String, Object> theMap; private MyOperatorTwoInner theInnerOp; private MyOperatorTwoInterface theInnerOpInterface; public String[] getTheStringArray() { return theStringArray; } public void setTheStringArray(String[] theStringArray) { this.theStringArray = theStringArray; } public int[] getTheIntArray() { return theIntArray; } public void setTheIntArray(int[] theIntArray) { this.theIntArray = theIntArray; } public Object[] getTheObjectArray() { return theObjectArray; } public void setTheObjectArray(Object[] theObjectArray) { this.theObjectArray = theObjectArray; } public Map<String, Object> getTheMap() { return theMap; } public void setTheMap(Map<String, Object> theMap) { this.theMap = theMap; } public MyOperatorTwoInner getTheInnerOp() { return theInnerOp; } public void setTheInnerOp(MyOperatorTwoInner theInnerOp) { this.theInnerOp = theInnerOp; } public MyOperatorTwoInterface getTheInnerOpInterface() { return theInnerOpInterface; } public void setTheInnerOpInterface(MyOperatorTwoInterface theInnerOpInterface) { this.theInnerOpInterface = theInnerOpInterface; } } public static class MyOperatorTwoInner { @DataFlowOpParameter private String fieldOne; @DataFlowOpParameter private int fieldTwo; } public static interface MyOperatorTwoInterface {} public static class MyOperatorTwoInterfaceImplOne implements MyOperatorTwoInterface {} public static class MyOperatorTwoInterfaceImplTwo implements MyOperatorTwoInterface {} }