package org.oddjob.jobs.structural; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.concurrent.Exchanger; import java.util.concurrent.Future; import junit.framework.TestCase; import org.apache.log4j.Logger; import org.oddjob.FailedToStopException; import org.oddjob.Loadable; import org.oddjob.Oddjob; import org.oddjob.OddjobLookup; import org.oddjob.OddjobSessionFactory; import org.oddjob.Stateful; import org.oddjob.Stoppable; import org.oddjob.Structural; import org.oddjob.arooa.ArooaSession; import org.oddjob.arooa.convert.ArooaConversionException; import org.oddjob.arooa.life.Configured; import org.oddjob.arooa.reflect.ArooaPropertyException; import org.oddjob.arooa.xml.XMLConfiguration; import org.oddjob.scheduling.DefaultExecutors; import org.oddjob.scheduling.MockExecutorService; import org.oddjob.scheduling.MockFuture; import org.oddjob.state.JobState; import org.oddjob.state.ParentState; import org.oddjob.structural.StructuralEvent; import org.oddjob.structural.StructuralListener; import org.oddjob.tools.OddjobTestHelper; import org.oddjob.tools.StateSteps; public class ForEachParallelTest extends TestCase { private static final Logger logger = Logger.getLogger( ForEachParallelTest.class); @Override protected void setUp() throws Exception { super.setUp(); logger.info("---------------------------------- " + getName() + " ---------------------------------------"); } public void testSimpleParallel() throws InterruptedException { DefaultExecutors defaultServices = new DefaultExecutors(); String xml = "<foreach id='test'>" + " <job>" + " <echo>${test.current}</echo>" + " </job>" + "</foreach>"; ForEachJob test = new ForEachJob(); test.setExecutorService(defaultServices.getPoolExecutor()); ArooaSession session = new OddjobSessionFactory().createSession(); test.setArooaSession(session); test.setConfiguration(new XMLConfiguration("XML", xml)); test.setValues(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)); test.setParallel(true); StateSteps state = new StateSteps(test); state.startCheck(ParentState.READY, ParentState.EXECUTING, ParentState.ACTIVE, ParentState.COMPLETE); test.run(); Object[] children = OddjobTestHelper.getChildren(test); assertEquals(10, children.length); state.checkWait(); test.destroy(); defaultServices.stop(); } public void testStop() throws InterruptedException, FailedToStopException { DefaultExecutors defaultServices = new DefaultExecutors(); String xml = "<foreach id='test'>" + " <job>" + " <wait name='${test.current}'/>" + " </job>" + "</foreach>"; ForEachJob test = new ForEachJob(); test.setExecutorService(defaultServices.getPoolExecutor()); ArooaSession session = new OddjobSessionFactory().createSession(); test.setArooaSession(session); test.setConfiguration(new XMLConfiguration("XML", xml)); test.setValues(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)); test.setParallel(true); test.load(); StateSteps state = new StateSteps(test); state.startCheck(ParentState.READY, ParentState.EXECUTING, ParentState.ACTIVE); Object[] children = OddjobTestHelper.getChildren(test); assertEquals(10, children.length); StateSteps[] childChecks = new StateSteps[10]; for (int i = 0; i < 10; ++i) { childChecks[i] = new StateSteps((Stateful) children[i]); childChecks[i].startCheck(JobState.READY, JobState.EXECUTING); } test.run(); state.checkNow(); // Ensure every child is executing before we stop them. for (int i = 0; i < 10; ++i) { childChecks[i].checkWait(); } state.startCheck(ParentState.ACTIVE, ParentState.COMPLETE); test.stop(); state.checkNow(); test.destroy(); defaultServices.stop(); } private static class MyExecutor extends MockExecutorService { List<Runnable> jobs = new ArrayList<Runnable>(); int cancels; @Override public Future<?> submit(Runnable task) { jobs.add(task); return new MockFuture<Void>() { @Override public boolean cancel(boolean mayInterruptIfRunning) { ++cancels; return false; } }; } } public void testStopWithSlowStartingChild() throws InterruptedException, FailedToStopException { MyExecutor executor = new MyExecutor(); String xml = "<foreach id='test'>" + " <job>" + " <echo>${test.current}</echo>" + " </job>" + "</foreach>"; ForEachJob test = new ForEachJob(); test.setExecutorService(executor); ArooaSession session = new OddjobSessionFactory().createSession(); test.setArooaSession(session); test.setConfiguration(new XMLConfiguration("XML", xml)); test.setValues(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)); test.setParallel(true); StateSteps state = new StateSteps(test); state.startCheck(ParentState.READY, ParentState.EXECUTING, ParentState.ACTIVE); test.run(); state.checkNow(); Object[] children = OddjobTestHelper.getChildren(test); assertEquals(10, children.length); assertEquals(10, executor.jobs.size()); // Only execute 5 jobs. for (int i = 0; i < 5; ++i) { executor.jobs.get(i).run(); } state.startCheck(ParentState.ACTIVE, ParentState.READY); test.stop(); state.checkNow(); assertEquals(10, executor.cancels); test.destroy(); } public void testExampleInOddjob() throws InterruptedException, FailedToStopException { Oddjob oddjob = new Oddjob(); oddjob.setConfiguration(new XMLConfiguration( "org/oddjob/jobs/structural/ForEachParallelExample.xml", getClass().getClassLoader())); oddjob.load(); Object foreach = OddjobTestHelper.getChildren(oddjob)[0]; ((Loadable) foreach).load(); Object[] children = OddjobTestHelper.getChildren(foreach); StateSteps wait1 = new StateSteps((Stateful) children[0]); StateSteps wait2 = new StateSteps((Stateful) children[1]); StateSteps wait3 = new StateSteps((Stateful) children[2]); wait1.startCheck(JobState.READY, JobState.EXECUTING); wait2.startCheck(JobState.READY, JobState.EXECUTING); wait3.startCheck(JobState.READY, JobState.EXECUTING); oddjob.run(); assertEquals(ParentState.ACTIVE, oddjob.lastStateEvent().getState()); wait1.checkWait(); wait2.checkWait(); wait3.checkWait(); oddjob.stop(); assertEquals(ParentState.READY, oddjob.lastStateEvent().getState()); oddjob.destroy(); } static final int BIG_LIST_SIZE = 20; public static class BigList implements Iterable<Integer> { private int listSize = BIG_LIST_SIZE; List<Integer> theList = new ArrayList<Integer>(); @Configured public void afterConfigure() { for (int i = 0; i < listSize; ++i) { theList.add(new Integer(i)); } } @Override public Iterator<Integer> iterator() { return theList.iterator(); } public int getListSize() { return listSize; } public void setListSize(int listSize) { this.listSize = listSize; } } private class ChildTracker implements StructuralListener { List<Object> children = new ArrayList<Object>(); Exchanger<Stateful> lastChild; @Override public void childAdded(StructuralEvent event) { children.add(event.getIndex(), event.getChild()); if (lastChild != null) { try { logger.info("* Waiting to Exchange " + event.getChild().toString()); lastChild.exchange((Stateful) event.getChild()); } catch (InterruptedException e) { throw new RuntimeException(e); } } } @Override public void childRemoved(StructuralEvent event) { children.remove(event.getIndex()); } } public void testParallelWithWindow() throws FailedToStopException, ArooaPropertyException, ArooaConversionException, InterruptedException { Oddjob oddjob = new Oddjob(); oddjob.setConfiguration(new XMLConfiguration( "org/oddjob/jobs/structural/ForEachParallelWithWindow.xml", getClass().getClassLoader())); StateSteps oddjobState = new StateSteps(oddjob); oddjobState.startCheck(ParentState.READY, ParentState.EXECUTING, ParentState.ACTIVE, ParentState.COMPLETE); oddjob.run(); OddjobLookup lookup = new OddjobLookup(oddjob); Structural foreach = lookup.lookup("foreach", Structural.class); int preLoad = lookup.lookup("foreach.preLoad", int.class); ChildTracker tracker = new ChildTracker(); foreach.addStructuralListener(tracker); List<?> children = tracker.children; assertEquals(preLoad, children.size()); while (((Stateful) children.get( children.size() - preLoad)).lastStateEvent().getState() != JobState.EXECUTING) { Thread.sleep(20); } tracker.lastChild = new Exchanger<Stateful>(); for (int index = 1; index < BIG_LIST_SIZE - 1; ++index) { if (index < 5) { for (int i = 0; i < index; ++i) { assertEquals("Wait " + i, children.get(i).toString()); } } else if (index < 1000) { for (int i = index - 4; i < index; ++i) { assertEquals("Wait " + i, children.get(i - (index - 4)).toString()); } } ((Stoppable) children.get(children.size() - 2)).stop(); logger.info("* Waiting for new child after index " + index); Stateful lastChild = tracker.lastChild.exchange(null); while (lastChild.lastStateEvent().getState() != JobState.EXECUTING) { Thread.sleep(20); } } ((Stoppable) children.get(children.size() - 2)).stop(); ((Stoppable) children.get(children.size() - 1)).stop(); oddjobState.checkWait(); oddjob.destroy(); } public void testParallelWithWindowStop() throws FailedToStopException, ArooaPropertyException, ArooaConversionException, InterruptedException { Oddjob oddjob = new Oddjob(); oddjob.setConfiguration(new XMLConfiguration( "org/oddjob/jobs/structural/ForEachParallelWithWindow.xml", getClass().getClassLoader())); oddjob.load(); Stateful foreach = new OddjobLookup(oddjob).lookup("foreach", Stateful.class); ((Loadable) foreach).load(); Object[] children = OddjobTestHelper.getChildren(foreach); assertEquals(2, children.length); assertEquals("Wait 0", children[0].toString()); assertEquals("Wait 1", children[1].toString()); StateSteps wait1States = new StateSteps((Stateful) children[0]); StateSteps wait2States = new StateSteps((Stateful) children[1]); wait1States.startCheck(JobState.READY, JobState.EXECUTING); wait2States.startCheck(JobState.READY, JobState.EXECUTING); oddjob.run(); wait1States.checkWait(); wait2States.checkWait(); ((Stoppable) foreach).stop(); assertEquals(ParentState.COMPLETE, ((Stateful) foreach).lastStateEvent().getState()); children = OddjobTestHelper.getChildren(foreach); assertEquals(2, children.length); assertEquals("Wait 0", children[0].toString()); assertEquals("Wait 1", children[1].toString()); assertEquals(ParentState.COMPLETE, oddjob.lastStateEvent().getState()); oddjob.destroy(); } }