package streamcruncher.test.func.generic; import java.sql.Timestamp; import java.util.HashSet; import java.util.List; import org.testng.Assert; import streamcruncher.api.artifact.RowSpec; import streamcruncher.api.artifact.RowSpec.Info; import streamcruncher.test.func.BatchResult; import streamcruncher.test.func.MultiStreamEventGenerator; /* * Author: Ashwin Jayaprakash Date: Mar 14, 2007 Time: 8:10:02 PM */ /** * <p> * This Test demonstrates the use of the Pattern matching features (Multi-Stream * Correlation). Events arrive from 3 different Streams, which simulates Objects * bearing a unique numbers and passing through 3 different stages. The * <code>when..present(..)..</code> clause is used to succinctly describe the * Patterns to watch for. * </p> * <p> * 3 "watch-patterns" are defined on the 3 Streams. A 5 second Partition Window * is defined on each of the 3 Streams. * </p> * <p> * Pattern 1) An Event appears in Stage1 and then it arrives in Stage2 but never * arrives in Stage3. Even if it does, the Event has expired from Stages 1 * and/or 2. * </p> * <p> * Pattern 2) An Event appears in Stage1 and then it arrives in Stage3 but never * arrives in Stage2. Even if it does, the Event has expired from Stages 1 * and/or 3. * </p> * <p> * Pattern 3) An Event appears in Stage1, Stage2 and Stage3 - all before it * expires from any of the 3 Stages. * </p> * To demonstrate that the other SQL features can still be used in the * Alert-Query, the Select clause uses a <code>case..when</code> clause to add * a comment based on the absence/presence of columns. */ public abstract class MultiStreamEventGeneratorTest extends MultiStreamEventGenerator { @Override protected Event[] createEventArray() { /** * Timestamp column will be filled later. */ return new Event[] { new Event(EventType.stg1_event, new Object[] { "TX", "Austin", null, 100L }, 3), new Event(EventType.stg1_event, new Object[] { "TX", "Austin", null, 200L }, 3), new Event(EventType.stg1_event, new Object[] { "TX", "Austin", null, 300L }, 3), new Event(EventType.stg1_event, new Object[] { "TX", "Austin", null, 400L }, 3), new Event(EventType.pause, new Object[] { 2500L }, -1), new Event(EventType.stg2_event, new Object[] { "TX", "Austin", null, 100L }, 3), new Event(EventType.stg2_event, new Object[] { "TX", "Austin", null, 200L }, 3), new Event(EventType.stg2_event, new Object[] { "TX", "Austin", null, 300L }, 3), new Event(EventType.stg2_event, new Object[] { "TX", "Austin", null, 400L }, 3), new Event(EventType.pause, new Object[] { 250L }, -1), new Event(EventType.stg3_event, new Object[] { "TX", "Austin", null, 100L }, 3), new Event(EventType.stg3_event, new Object[] { "TX", "Austin", null, 400L }, 3), new Event(EventType.pause, new Object[] { 4000L }, -1), new Event(EventType.stg3_event, new Object[] { "TX", "Austin", null, 500L }, 3), new Event(EventType.stg3_event, new Object[] { "TX", "Austin", null, 600L }, 3), new Event(EventType.stg3_event, new Object[] { "TX", "Austin", null, 700L }, 3), new Event(EventType.stg3_event, new Object[] { "TX", "Austin", null, 800L }, 3), new Event(EventType.stg3_event, new Object[] { "TX", "Austin", null, 900L }, 3), new Event(EventType.stg3_event, new Object[] { "TX", "Austin", null, 1000L }, 3), new Event(EventType.pause, new Object[] { 2500L }, -1), new Event(EventType.stg2_event, new Object[] { "TX", "Austin", null, 500L }, 3), new Event(EventType.stg2_event, new Object[] { "TX", "Austin", null, 600L }, 3), new Event(EventType.stg2_event, new Object[] { "TX", "Austin", null, 900L }, 3), new Event(EventType.pause, new Object[] { 250L }, -1), new Event(EventType.stg1_event, new Object[] { "TX", "Austin", null, 500L }, 3), new Event(EventType.stg1_event, new Object[] { "TX", "Austin", null, 600L }, 3), new Event(EventType.stg1_event, new Object[] { "TX", "Austin", null, 700L }, 3), new Event(EventType.stg1_event, new Object[] { "TX", "Austin", null, 800L }, 3), new Event(EventType.stg1_event, new Object[] { "TX", "Austin", null, 1000L }, 3), new Event(EventType.pause, new Object[] { 2500L }, -1), new Event(EventType.stg3_event, new Object[] { "TX", "Austin", null, 1100L }, 3), new Event(EventType.stg1_event, new Object[] { "TX", "Austin", null, 1100L }, 3), new Event(EventType.pause, new Object[] { 6500L }, -1), // Stage1 arrives, but not in time. new Event(EventType.stg2_event, new Object[] { "TX", "Austin", null, 1100L }, 3), new Event(EventType.pause, new Object[] { 2500L }, -1), new Event(EventType.stg3_event, new Object[] { "TX", "Austin", null, 1200L }, 3), new Event(EventType.stg2_event, new Object[] { "TX", "Austin", null, 1200L }, 3), new Event(EventType.stg1_event, new Object[] { "TX", "Austin", null, 1200L }, 3) }; } @Override protected String[] getResultColumnTypes() { return new String[] { java.lang.Long.class.getName(), java.lang.Long.class.getName(), java.lang.Long.class.getName(), RowSpec.addInfo(java.lang.String.class.getName(), Info.SIZE, 10), RowSpec.addInfo(java.lang.String.class.getName(), Info.SIZE, 10) }; } @Override protected String[] getStage1ColumnTypes() { return new String[] { RowSpec.addInfo(java.lang.String.class.getName(), Info.SIZE, 10), RowSpec.addInfo(java.lang.String.class.getName(), Info.SIZE, 10), java.sql.Timestamp.class.getName(), java.lang.Long.class.getName() }; } @Override protected String[] getStage2ColumnTypes() { return new String[] { RowSpec.addInfo(java.lang.String.class.getName(), Info.SIZE, 10), RowSpec.addInfo(java.lang.String.class.getName(), Info.SIZE, 10), java.sql.Timestamp.class.getName(), java.lang.Long.class.getName() }; } @Override protected String[] getStage3ColumnTypes() { return new String[] { RowSpec.addInfo(java.lang.String.class.getName(), Info.SIZE, 10), RowSpec.addInfo(java.lang.String.class.getName(), Info.SIZE, 10), java.sql.Timestamp.class.getName(), java.lang.Long.class.getName() }; } @Override protected String[] getResultColumnNames() { return new String[] { "stg1_id", "stg2_id", "stg3_id", "levelb", "comments" }; } @Override protected String getRQLColumnsCSV() { String[] columns = getResultColumnNames(); // Insert a case..when script pseudo column at the end as "comments". columns[4] = "case" + " when stg2_id is null and stg3_id is not null then 'Stage 2 missing!'" + " when stg2_id is not null and stg3_id is null then 'Stage 3 missing!'" + " when stg2_id is null and stg3_id is null then 'Stage 2 & 3 missing!'" + " else 'All OK!' end as " + columns[4]; String csv = ""; for (int i = 0; i < columns.length; i++) { if (csv != "") { csv = csv + ", "; } csv = csv + columns[i]; } return csv; } @Override protected String getRQL() { String csv = getRQLColumnsCSV(); return "select " + csv + " from" + " alert one_event.event_id as stg1_id," + " two_event.event_id as stg2_id," + " three_event.event_id as stg3_id," + " one_event.levelb as levelb" + " using stg1_event (partition store last 5 seconds) as one_event correlate on event_id," + " stg2_event (partition store last 5 seconds) as two_event correlate on event_id," + " stg3_event (partition store last 5 seconds) as three_event correlate on event_id" + " when present(one_event and two_event and not three_event)" + " or present(one_event and not two_event and three_event)" + " or present(one_event and two_event and three_event);"; } protected HashSet<String> getExpectedResults() { HashSet<String> expectedResults = new HashSet<String>(); expectedResults.add("100 100 100 Austin"); expectedResults.add("200 200 null Austin"); expectedResults.add("300 300 null Austin"); expectedResults.add("400 400 400 Austin"); expectedResults.add("500 500 500 Austin"); expectedResults.add("600 600 600 Austin"); expectedResults.add("700 null 700 Austin"); expectedResults.add("800 null 800 Austin"); expectedResults.add("1000 null 1000 Austin"); expectedResults.add("1100 null 1100 Austin"); expectedResults.add("1200 1200 1200 Austin"); return expectedResults; } @Override protected void verify(List<BatchResult> results) { HashSet<String> expectedResults = getExpectedResults(); System.out.println("--Results--"); for (BatchResult result : results) { System.out.println("Batch created at: " + new Timestamp(result.getTimestamp()) + ". Rows: " + result.getRows().size()); List<Object[]> rows = result.getRows(); System.out.println(" Batch results"); for (Object[] objects : rows) { System.out.print(" "); for (Object object : objects) { System.out.print(object + " "); } /* * "stg1_id", "stg2_id", "stg3_id", "levelb", "comment". */ Long stg1Id = objects[0] == null ? null : ((Number) objects[0]).longValue(); Long stg2Id = objects[1] == null ? null : ((Number) objects[1]).longValue(); Long stg3Id = objects[2] == null ? null : ((Number) objects[2]).longValue(); String levelb = (String) objects[3]; String hash = stg1Id + " " + stg2Id + " " + stg3Id + " " + levelb; boolean exists = expectedResults.remove(hash); Assert.assertTrue(exists, "Pattern " + hash + " does not match expected results"); System.out.println(); } } Assert.assertEquals(expectedResults.size(), 0, "All expected Patterns did not arrive"); } }