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.