How to process a request in Play

Play is a brand-new Web application framework for Java and Scala. However Play doesn't use Servlet. Instead, Play uses Netty for handling HTTP. In this post, I'll show you how to process a request in Play.

play.server.Server

play.server.Server has an entry point (main).

public static void main(String[] args) throws Exception {
    ...
        new Server(args);

First, Server creates org.jboss.netty.bootstrap.ServerBootstrap and play.server.HttpServerPipelineFactory.

ServerBootstrap bootstrap = new ServerBootstrap(new NioServerSocketChannelFactory(
        Executors.newCachedThreadPool(), Executors.newCachedThreadPool())
);
try {
    if (httpPort != -1) {
        bootstrap.setPipelineFactory(new HttpServerPipelineFactory());
        bootstrap.bind(new InetSocketAddress(address, httpPort));
        bootstrap.setOption("child.tcpNoDelay", true);

And HttpServerPipelineFactory adds play.server.PlayHandler into Netty's pipeline.

PlayHandler receives a message from Netty, and creates play.server.NettyInvocation and pass it to play.Invoker.invoke.

// Deleguate to Play framework
Invoker.invoke(new NettyInvocation(request, response, ctx, nettyRequest, e));

play.server.NettyInvocation

NettyInvocation#init handles static files, NettyInvocation#getInvocationContext and NettyInvocation#execute calls ActionInvoker to handle other request.

play.mvc.ActionInvoker

ActionInvoker determines method, and call it. play.mvc.Controller#render returns an result as an exception.

public static void invoke(Http.Request request, Http.Response response) {
    Monitor monitor = null;
     try {

        resolve(request, response);
        Method actionMethod = request.invokedMethod;

        ...

        // 3. Invoke the action
        try {
            // @Before
            handleBefores(request);

            // Action

            Result actionResult = null;
            String cacheKey = null;

            ...

            if (actionResult == null) {
                ControllerInstrumentation.initActionCall();
                try {
                    inferResult(invokeControllerMethod(actionMethod));
                } catch (InvocationTargetException ex) {
                    // It's a Result ? (expected)
                    if (ex.getTargetException() instanceof Result) {
                        actionResult = (Result) ex.getTargetException();
                        // Cache it if needed
                        if (cacheKey != null) {
                            ...
                        }

                    } else {
                        ...
                        throw ex;
                    }
                }
            }

           ...

            // OK, re-throw the original action result
            if (actionResult != null) {
                throw actionResult;
            }

            throw new NoResult();

        } catch (IllegalAccessException ex) {
            throw ex;
        } catch (IllegalArgumentException ex) {
            throw ex;
        } catch (InvocationTargetException ex) {
            // It's a Result ? (expected)
            if (ex.getTargetException() instanceof Result) {
                throw (Result) ex.getTargetException();
            }

            ...

            throw new JavaExecutionException(Http.Request.current().action, ex);
        }

    } catch (Result result) {

        Play.pluginCollection.onActionInvocationResult(result);

        // OK there is a result to apply
        // Save session & flash scope now

        Scope.Session.current().save();
        Scope.Flash.current().save();

        result.apply(request, response);

        Play.pluginCollection.afterActionInvocation();

        // @Finally
        handleFinallies(request, null);

    } catch (PlayException e) {
        handleFinallies(request, e);
        throw e;
    } catch (Throwable e) {
        handleFinallies(request, e);
        throw new UnexpectedException(e);
    } finally {
        if (monitor != null) {
            monitor.stop();
        }
    }
}

Honestly, I don't know why Play uses an exception. It's not straightforward for me.