package com.mastfrog.acteur; import com.google.inject.AbstractModule; import com.google.inject.ConfigurationException; import com.google.inject.Inject; import com.google.inject.name.Named; import com.mastfrog.acteur.Acteur.RespondWith; import com.mastfrog.acteur.ActeurFactory.Test; import com.mastfrog.acteur.errors.Err; import com.mastfrog.acteur.errors.ErrorRenderer; import com.mastfrog.acteur.errors.ErrorResponse; import com.mastfrog.acteur.errors.ExceptionEvaluator; import com.mastfrog.acteur.errors.ExceptionEvaluatorRegistry; import com.mastfrog.acteur.headers.Headers; import com.mastfrog.acteur.headers.Method; import com.mastfrog.acteur.server.ServerModule; import com.mastfrog.acteur.util.ErrorInterceptor; import com.mastfrog.acteurbase.Chain; import com.mastfrog.netty.http.test.harness.TestHarness; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; import io.netty.handler.codec.http.HttpResponseStatus; import static io.netty.handler.codec.http.HttpResponseStatus.OK; import io.netty.util.CharsetUtil; import java.io.IOException; import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import static org.junit.Assert.assertNotNull; import org.openide.util.Exceptions; /** * * @author tim */ public class CompApp extends Application { public CompApp() { add(IterPage.class); add(Unchunked.class); add(Echo.class); add(DeferredOutput.class); add(Branch.class); add(Fails.class); add(NoContentPage.class); add(DynPage.class); } @Override public void onError(Throwable err) { System.out.println("OUCH! "); this.err = err; } static volatile Throwable err; static void throwIfError() throws Throwable { Throwable old = err; err = null; if (old != null) { throw old; } } static final class Module extends AbstractModule { @Override protected void configure() { install(new ServerModule<CompApp>(CompApp.class)); bind(ErrorInterceptor.class).to(TestHarness.class); bind(ExceptionEval.class).asEagerSingleton(); bind(ErrorRenderer.class).to(ExceptionRen.class); } } static class ExceptionEval extends ExceptionEvaluator { @Inject public ExceptionEval(ExceptionEvaluatorRegistry registry) { super(registry); } @Override public ErrorResponse evaluate(Throwable t, Acteur acteur, Page page, HttpEvent evt) { System.out.println("EVALUATE " + t.getClass().getName()); if (t instanceof ConfigurationException) { // if (page instanceof Fails) { return Err.conflict("werg"); // } } return null; } } static class ExceptionRen extends ErrorRenderer { @Override @SuppressWarnings("unchecked") public String render(ErrorResponse resp, HttpEvent evt) throws IOException { Map<String, Object> m = (Map<String, Object>) resp.message(); String s = (String) m.get("error"); assertNotNull(s); if ("werg".equals(s)) { return "Hoober"; } return null; } } static class NotBoundThing { NotBoundThing(String foo) { } } static class CannotCreateMe { CannotCreateMe(NotBoundThing t) { } } private static class Fails extends Page { @Inject Fails(ActeurFactory af) { add(af.matchPath("^fail$")); add(af.matchMethods(Method.GET)); add(Failer.class); } static class Failer extends Acteur { @Inject Failer(CannotCreateMe x) { ok("Oh no!"); } } } private static final class Branch extends Page { @Inject Branch(ActeurFactory af) { add(af.matchMethods(Method.GET)); add(af.matchPath("^branch$")); add(af.branch(ABranch.class, BBranch.class, new Test() { @Override public boolean test(HttpEvent evt) { System.out.println("TEST"); return "true".equals(evt.getParameter("a")); } })); } private static class ABranch extends Acteur { @Inject ABranch() { System.out.println("abranch"); ok("A"); } } private static class BBranch extends Acteur { @Inject BBranch() { System.out.println("bbranch"); ok("B"); } } } private static final class Echo extends Page { @Inject Echo(ActeurFactory af) { add(af.matchMethods(Method.POST)); add(af.matchPath("^echo$")); add(EchoActeur.class); } private static final class EchoActeur extends Acteur { @Inject EchoActeur(HttpEvent evt, ContentConverter cvt) throws IOException { // if (!evt.getContent().isReadable()) { // setState(new RespondWith(400, "Content not readable")); // return; // } else if (evt.getContent().readableBytes() <= 0) { // setState(new RespondWith(HttpResponseStatus.EXPECTATION_FAILED, "Zero byte content")); // return; // } String content = cvt.toObject(evt.getContent(), evt.getHeader(Headers.CONTENT_TYPE), String.class); System.out.println("SEND MESSAGE '" + content + "'"); setState(new RespondWith(200, content)); } } } private static final class DeferredOutput extends Page { @Inject DeferredOutput(ActeurFactory af) { add(af.matchMethods(Method.GET)); add(af.matchPath("^deferred")); add(DeferActeur.class); } private static class DeferActeur extends Acteur { @Inject DeferActeur(HttpEvent evt) { System.out.println("DEFER ACTEUR RUN"); setResponseWriter(DeferredOutputWriter.class); setState(new RespondWith(200)); } } private static class DeferredOutputWriter extends ResponseWriter implements Runnable { private Output out; @Override public synchronized void run() { try { Thread.sleep(3000); } catch (InterruptedException ex) { Exceptions.printStackTrace(ex); } try { out.write("I guess it's okay now"); out.channel().close(); } catch (IOException ex) { Exceptions.printStackTrace(ex); } } @Override public synchronized Status write(Event<?> evt, Output out) throws Exception { System.out.println("Deferring write"); this.out = out; Thread t = new Thread(this); t.setDaemon(true); t.start(); return Status.DEFERRED; } } } private static final class Unchunked extends Page { @Inject Unchunked(ActeurFactory af) { add(af.matchMethods(Method.GET)); add(af.matchPath("^unchunked")); add(af.requireParameters("iters")); add(OldStyleActeur.class); } private static final class OldStyleActeur extends Acteur implements ChannelFutureListener { private String msg; private Integer max; private final ExecutorService svc; @Inject OldStyleActeur(HttpEvent evt, @Named(ServerModule.BACKGROUND_THREAD_POOL_NAME) ExecutorService svc) { this.svc = svc; max = evt.getIntParameter("iters").get(); if (max == null) { max = 5; } System.out.println("Created an iterWriter"); msg = evt.getParameter("msg"); if (msg == null) { msg = "Iteration "; } setChunked(false); setResponseBodyWriter(this); setState(new RespondWith(200)); } int iteration = 0; volatile int entryCount = 0; @Override public void operationComplete(final ChannelFuture future) throws Exception { if (entryCount > 0) { svc.submit(new Callable<Void>() { @Override public Void call() throws Exception { operationComplete(future); return null; } }); return; } ChannelFuture f = future; entryCount++; try { System.out.println("Iteration " + iteration + "\n"); f = f.channel().writeAndFlush(Unpooled.copiedBuffer(msg + iteration + "\n", CharsetUtil.UTF_8)); if (iteration++ < max) { f.addListener(this); } else { System.out.println("Close channel"); future.addListener(CLOSE); } } finally { entryCount--; } } } } private static final class IterPage extends Page { @Inject IterPage(ActeurFactory af) { add(af.matchMethods(Method.GET)); add(af.matchPath("^iter$")); add(af.requireParameters("iters")); add(IterActeur.class); } static class IterActeur extends Acteur { @Inject IterActeur(HttpEvent evt) { setResponseWriter(IterWriter.class); setState(new RespondWith(OK)); } static class IterWriter extends ResponseWriter { private final int max; private String msg; @Inject IterWriter(HttpEvent evt) { max = evt.getIntParameter("iters").get(); System.out.println("Created an iterWriter"); msg = evt.getParameter("msg"); if (msg == null) { msg = "Iteration "; } } @Override public Status write(Event<?> evt, Output out, int iteration) throws Exception { System.out.println("Iteration " + iteration + "\n"); out.write(msg + iteration + "\n"); if (iteration < max) { return Status.NOT_DONE; } else { return Status.DONE; } } } } } public static class NoContentPage extends Page { @Inject NoContentPage(ActeurFactory af) { add(af.matchMethods(Method.GET)); add(af.matchPath("^nothing$")); add(NoActeur.class); } static class NoActeur extends Acteur { @Inject NoActeur() { setState(new RespondWith(HttpResponseStatus.PAYMENT_REQUIRED)); } } } public static class DynPage extends Page { @Inject DynPage(ActeurFactory af) { add(af.matchPath("dyn$")); add(af.matchMethods(Method.GET)); add(FirstActeur.class); } } public static class FirstActeur extends Acteur { @Inject FirstActeur(Chain<Acteur> chain) { chain.add(SecondActeur.class); next(); } } public static class SecondActeur extends Acteur { @Inject SecondActeur(Chain<Acteur> chain) { chain.add(ThirdActeur.class); next(); } } public static class ThirdActeur extends Acteur { @Inject ThirdActeur() { ok("Dynamic acteur"); } } }