/* * The MIT License * * Copyright 2015 Tim Boudreau. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package com.mastfrog.acteurbase; import com.mastfrog.acteurbase.impl.A2; import com.mastfrog.acteurbase.impl.Response; import com.mastfrog.acteurbase.impl.ResponseImpl; import com.google.inject.AbstractModule; import com.google.inject.Inject; import com.mastfrog.acteurbase.Deferral.Resumer; import com.mastfrog.acteur.headers.Headers; import com.mastfrog.giulius.Dependencies; import com.mastfrog.giulius.ShutdownHookRegistry; import com.mastfrog.guicy.scope.ReentrantScope; import io.netty.handler.codec.http.HttpResponseStatus; import java.io.IOException; import java.util.LinkedList; import java.util.List; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.ExecutorService; import java.util.concurrent.atomic.AtomicBoolean; import org.junit.After; import org.junit.Before; import org.junit.Test; /** * * @author Tim Boudreau */ public class ChainRunnerTest { ReentrantScope scope; Dependencies deps; ExecutorService svc; ArrayChain<AbstractActeur<Response, ResponseImpl, ActeurState<Response, ResponseImpl>>> chain; ArrayChain<AbstractActeur<Response, ResponseImpl, ActeurState<Response, ResponseImpl>>> rejectIt; ArrayChain<AbstractActeur<Response, ResponseImpl, ActeurState<Response, ResponseImpl>>> dontRespond; ArrayChain<AbstractActeur<Response, ResponseImpl, ActeurState<Response, ResponseImpl>>> plainChain; ArrayChain<AbstractActeur<Response, ResponseImpl, ActeurState<Response, ResponseImpl>>> errorChain; Timer timer = new Timer(); @Test(timeout = 10000) public void testChainRunner() throws Exception, Throwable { AtomicBoolean cancelled = new AtomicBoolean(); ChainRunner cr = new ChainRunner(svc, scope); TestCallback chainWithDeferResults = new TestCallback(); TestCallback dontRespondChainResults = new TestCallback(); TestCallback plainChainResults = new TestCallback(); TestCallback errorChainResults = new TestCallback(); TestCallback rejectChainResults = new TestCallback(); try (AutoCloseable cl = scope.enter()) { cr.submit(chain, chainWithDeferResults, cancelled); cr.submit(dontRespond, dontRespondChainResults, cancelled); cr.submit(plainChain, plainChainResults, cancelled); cr.submit(errorChain, errorChainResults, cancelled); cr.submit(rejectIt, rejectChainResults, cancelled); } chainWithDeferResults.assertGotResponse().assertActeurClass(AddedA.class).throwIfError().assertNotRejected(); dontRespondChainResults.assertNoResponse().throwIfError(); plainChainResults.throwIfError().assertGotResponse().assertActeurClass(AddedA.class).assertNotRejected(); errorChainResults.assertException(SpecialError.class); rejectChainResults.throwIfError().assertRejected(); } @Test(timeout = 20000) public void testMultiChainRunner() throws Exception, Throwable { AtomicBoolean cancelled = new AtomicBoolean(); List<ArrayChain<AbstractActeur<Response, ResponseImpl, ActeurState<Response, ResponseImpl>>>> l = new LinkedList<>(); for (int i = 0; i < 5; i++) { ArrayChain<AbstractActeur<Response, ResponseImpl, ActeurState<Response, ResponseImpl>>> ch = new NamedChain("Chain " + i, deps, AbstractActeur.class) .add(FirstA.class).add(Rejecter.class).add(SecondWithoutTimeoutA.class).add(EndA.class); l.add(ch); } l.add(plainChain); ChainsRunner cr = new ChainsRunner(svc, scope, new ChainRunner(svc, scope)); TestCallback callback = new TestCallback(); cr.submit(l, callback, cancelled); callback.throwIfError().assertNotRejected().assertGotResponse(); l.remove(l.size() - 1); callback = new TestCallback(); cr.submit(l, callback, cancelled); callback.throwIfError().assertNoResponse(); } static class NamedChain extends ArrayChain<AbstractActeur<Response, ResponseImpl, ActeurState<Response, ResponseImpl>>> { private final String name; public NamedChain(String name, Dependencies deps, Class<? super AbstractActeur<Response, ResponseImpl, ActeurState<Response, ResponseImpl>>> type) { super(deps, type); this.name = name; } public String toString() { return name; } } @Before public void before() throws IOException { // svc = Executors.newCachedThreadPool(); svc = new java.util.concurrent.ForkJoinPool(12); scope = new ReentrantScope(); deps = new Dependencies(new M()); ShutdownHookRegistry reg = deps.getInstance(ShutdownHookRegistry.class); reg.add(svc); reg.add(timer); chain = new NamedChain("Simple", deps, AbstractActeur.class) .add(FirstA.class).add(SecondA.class).add(FinalA.class); rejectIt = new NamedChain("RejectIt", deps, AbstractActeur.class) .add(FirstA.class).add(Rejecter.class).add(SecondA.class).add(FinalA.class); dontRespond = new NamedChain("DontRespond", deps, AbstractActeur.class) .add(FirstA.class).add(SecondWithoutTimeoutA.class); plainChain = new NamedChain("Plain", deps, AbstractActeur.class) .add(FirstA.class).add(SecondWithoutTimeoutA.class).add(FinalA.class); errorChain = new NamedChain("ErrorChain", deps, AbstractActeur.class) .add(FirstA.class).add(SecondWithoutTimeoutA.class).add(ErrorA.class); } @After public void after() { deps.shutdown(); } class M extends AbstractModule { @Override protected void configure() { scope.bindTypes(binder(), Deferral.class, Chain.class, String.class, Integer.class, Byte.class, StringBuilder.class, Short.class, Float.class, Double.class); bind(ExecutorService.class).toInstance(svc); bind(ReentrantScope.class).toInstance(scope); bind(Timer.class).toInstance(timer); } } static class Rejecter extends A2 { Rejecter() { super.reject(); } } static class FirstA extends A2 { FirstA() { System.out.println("firstA " + Thread.currentThread()); setState(new ActeurState<Response, ResponseImpl>("hello")); } } static class SecondA extends A2 { @Inject SecondA(String msg, Timer timer, Deferral defer) { System.out.println("secondA " + msg + " " + Thread.currentThread()); response().add(Headers.CONTENT_ENCODING, "foo " + msg); setState(new ActeurState<Response, ResponseImpl>(23)); System.out.println("defer"); final Resumer resume = defer.defer(); timer.schedule(new TimerTask() { @Override public void run() { System.out.println("timer task run"); resume.resume(); } }, 1000); } } static class SecondWithoutTimeoutA extends A2 { @Inject SecondWithoutTimeoutA(String msg) { System.out.println("secondA " + msg + " " + Thread.currentThread()); response().add(Headers.CONTENT_ENCODING, "foo " + msg); setState(new ActeurState<Response, ResponseImpl>(23)); } } static class FinalA extends A2 { @Inject @SuppressWarnings("unchecked") FinalA(String msg, Integer val, Chain chain) { System.out.println("finalA " + val + " " + Thread.currentThread()); response().add(Headers.ACCEPT, "3 "); setState(new ActeurState<Response, ResponseImpl>(0.5F)); chain.add(AddedA.class); chain.add(ErrorA.class); // will not be called because AddedA sets teh response code } } static class AddedA extends A2 { @Inject AddedA(Float f) { System.out.println("AddedA " + f + " " + Thread.currentThread()); response().setStatus(HttpResponseStatus.CREATED); setState(new ActeurState<Response, ResponseImpl>(false)); } } static class EndA extends A2 { EndA() { response().setMessage("foo"); response().setStatus(HttpResponseStatus.OK); setState( new ActeurState<Response, ResponseImpl>(false)); } } static class ErrorA extends A2 { ErrorA() throws SpecialError { throw new SpecialError("Ha ha."); } } static class SpecialError extends Exception { SpecialError(String s) { super(s); } } }