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.