/* *************************************************************************************** * 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.filter; import com.espertech.esper.client.EventBean; import com.espertech.esper.client.EventType; import com.espertech.esper.supportunit.bean.SupportBean; import com.espertech.esper.supportunit.event.SupportEventBeanFactory; import com.espertech.esper.supportunit.event.SupportEventTypeFactory; import com.espertech.esper.supportunit.filter.IndexTreeBuilderRunnable; import com.espertech.esper.supportunit.filter.SupportFilterHandle; import com.espertech.esper.supportunit.filter.SupportFilterSpecBuilder; import junit.framework.TestCase; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.ArrayDeque; import java.util.LinkedList; import java.util.List; import java.util.Vector; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantReadWriteLock; public class TestIndexTreeBuilderMultithreaded extends TestCase { private Vector<FilterSpecCompiled> testFilterSpecs; private Vector<EventBean> matchedEvents; private Vector<EventBean> unmatchedEvents; private EventType eventType; private FilterHandleSetNode topNode; private List<FilterHandle> filterCallbacks; private List<ArrayDeque<EventTypeIndexBuilderIndexLookupablePair>> pathsAddedTo; private FilterServiceGranularLockFactory lockFactory = new FilterServiceGranularLockFactoryReentrant(); public void setUp() { eventType = SupportEventTypeFactory.createBeanType(SupportBean.class); topNode = new FilterHandleSetNode(new ReentrantReadWriteLock()); filterCallbacks = new LinkedList<FilterHandle>(); pathsAddedTo = new LinkedList<ArrayDeque<EventTypeIndexBuilderIndexLookupablePair>>(); testFilterSpecs = new Vector<FilterSpecCompiled>(); matchedEvents = new Vector<EventBean>(); unmatchedEvents = new Vector<EventBean>(); // Any int and double value specified here must match only the current filter spec not any other filter spec testFilterSpecs.add(makeSpec(new Object[]{"intPrimitive", FilterOperator.GREATER_OR_EQUAL, 100000})); matchedEvents.add(makeEvent(9999999, -1)); unmatchedEvents.add(makeEvent(0, -1)); testFilterSpecs.add(makeSpec(new Object[]{"intPrimitive", FilterOperator.GREATER_OR_EQUAL, 10, "doublePrimitive", FilterOperator.EQUAL, 0.5})); matchedEvents.add(makeEvent(10, 0.5)); unmatchedEvents.add(makeEvent(0, 0.5)); testFilterSpecs.add(makeSpec(new Object[]{"doublePrimitive", FilterOperator.EQUAL, 0.8})); matchedEvents.add(makeEvent(-1, 0.8)); unmatchedEvents.add(makeEvent(-1, 0.1)); testFilterSpecs.add(makeSpec(new Object[]{"doublePrimitive", FilterOperator.EQUAL, 99.99, "intPrimitive", FilterOperator.LESS, 1})); matchedEvents.add(makeEvent(0, 99.99)); unmatchedEvents.add(makeEvent(2, 0.5)); testFilterSpecs.add(makeSpec(new Object[]{"doublePrimitive", FilterOperator.GREATER, .99, "intPrimitive", FilterOperator.EQUAL, 5001})); matchedEvents.add(makeEvent(5001, 1.1)); unmatchedEvents.add(makeEvent(5002, 0.98)); testFilterSpecs.add(makeSpec(new Object[]{"intPrimitive", FilterOperator.LESS, -99000})); matchedEvents.add(makeEvent(-99001, -1)); unmatchedEvents.add(makeEvent(-98999, -1)); testFilterSpecs.add(makeSpec(new Object[]{"intPrimitive", FilterOperator.GREATER_OR_EQUAL, 11, "doublePrimitive", FilterOperator.GREATER, 888.0})); matchedEvents.add(makeEvent(11, 888.001)); unmatchedEvents.add(makeEvent(10, 888)); testFilterSpecs.add(makeSpec(new Object[]{"intPrimitive", FilterOperator.EQUAL, 973, "doublePrimitive", FilterOperator.EQUAL, 709.0})); matchedEvents.add(makeEvent(973, 709)); unmatchedEvents.add(makeEvent(0, 0.5)); testFilterSpecs.add(makeSpec(new Object[]{"intPrimitive", FilterOperator.EQUAL, 973, "doublePrimitive", FilterOperator.EQUAL, 655.0})); matchedEvents.add(makeEvent(973, 655)); unmatchedEvents.add(makeEvent(33838, 655.5)); } public void testVerifyFilterSpecSet() { // Add all the above filter definitions for (FilterSpecCompiled filterSpec : testFilterSpecs) { FilterValueSet filterValues = filterSpec.getValueSet(null, null, null); FilterHandle callback = new SupportFilterHandle(); filterCallbacks.add(callback); pathsAddedTo.add(IndexTreeBuilder.add(filterValues, callback, topNode, lockFactory)[0]); } // None of the not-matching events should cause any match for (EventBean theEvent : unmatchedEvents) { List<FilterHandle> matches = new LinkedList<FilterHandle>(); topNode.matchEvent(theEvent, matches); assertTrue(matches.size() == 0); } // All of the matching events should cause exactly one match for (EventBean theEvent : matchedEvents) { List<FilterHandle> matches = new LinkedList<FilterHandle>(); topNode.matchEvent(theEvent, matches); assertTrue(matches.size() == 1); } // Remove all expressions previously added int count = 0; for (ArrayDeque<EventTypeIndexBuilderIndexLookupablePair> treePath : pathsAddedTo) { FilterHandle callback = filterCallbacks.get(count++); IndexTreeBuilder.remove(eventType, callback, treePath.toArray(new EventTypeIndexBuilderIndexLookupablePair[treePath.size()]), topNode); } // After the remove no matches are expected for (EventBean theEvent : matchedEvents) { List<FilterHandle> matches = new LinkedList<FilterHandle>(); topNode.matchEvent(theEvent, matches); assertTrue(matches.size() == 0); } } public void testMultithreaded() throws Exception { FilterHandleSetNode topNode = new FilterHandleSetNode(new ReentrantReadWriteLock()); performMultithreadedTest(topNode, 2, 1000, 1); performMultithreadedTest(topNode, 3, 1000, 1); performMultithreadedTest(topNode, 4, 1000, 1); performMultithreadedTest(new FilterHandleSetNode(new ReentrantReadWriteLock()), 2, 1000, 1); performMultithreadedTest(new FilterHandleSetNode(new ReentrantReadWriteLock()), 3, 1000, 1); performMultithreadedTest(new FilterHandleSetNode(new ReentrantReadWriteLock()), 4, 1000, 1); } private void performMultithreadedTest(FilterHandleSetNode topNode, int numberOfThreads, int numberOfRunnables, int numberOfSecondsSleep) throws Exception { log.info(".performMultithreadedTest Loading thread pool work queue,numberOfRunnables=" + numberOfRunnables); ThreadPoolExecutor pool = new ThreadPoolExecutor(0, numberOfThreads, 99999, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>()); for (int i = 0; i < numberOfRunnables; i++) { IndexTreeBuilderRunnable runnable = new IndexTreeBuilderRunnable(eventType, topNode, testFilterSpecs, matchedEvents, unmatchedEvents); pool.execute(runnable); } log.info(".performMultithreadedTest Starting thread pool, threads=" + numberOfThreads); pool.setCorePoolSize(numberOfThreads); // Sleep X seconds sleep(numberOfSecondsSleep); log.info(".performMultithreadedTest Completed, numberOfRunnables=" + numberOfRunnables + " numberOfThreads=" + numberOfThreads + " completed=" + pool.getCompletedTaskCount()); pool.shutdown(); pool.awaitTermination(1, TimeUnit.SECONDS); assertTrue(pool.getCompletedTaskCount() == numberOfRunnables); } private void sleep(int sec) { try { Thread.sleep(sec * 1000); } catch (InterruptedException e) { log.warn("Interrupted: {}", e.getMessage(), e); } } private FilterSpecCompiled makeSpec(Object[] args) { return SupportFilterSpecBuilder.build(eventType, args); } private EventBean makeEvent(int aInt, double aDouble) { SupportBean bean = new SupportBean(); bean.setIntPrimitive(aInt); bean.setDoublePrimitive(aDouble); return SupportEventBeanFactory.createObject(bean); } private static final Logger log = LoggerFactory.getLogger(TestIndexTreeBuilderMultithreaded.class); }