/sitebricks-async/src/main/java/com/google/sitebricks/SitebricksAsyncHandler.java

http://github.com/dhanji/sitebricks · Java · 149 lines · 113 code · 18 blank · 18 comment · 11 complexity · dc8dbb49adbb185611f434530d06d621 MD5 · raw file

  1. package com.google.sitebricks;
  2. import com.google.common.collect.Maps;
  3. import com.google.inject.Inject;
  4. import com.google.inject.Key;
  5. import com.google.inject.Provider;
  6. import com.google.inject.Singleton;
  7. import com.google.inject.servlet.ServletScopes;
  8. import com.google.sitebricks.headless.NettyReplyMaker;
  9. import com.google.sitebricks.headless.Reply;
  10. import com.google.sitebricks.headless.Request;
  11. import com.google.sitebricks.routing.RoutingDispatcher;
  12. import com.google.sitebricks.routing.RoutingDispatcher.Events;
  13. import org.jboss.netty.buffer.ChannelBuffers;
  14. import org.jboss.netty.channel.ChannelFuture;
  15. import org.jboss.netty.channel.ChannelFutureListener;
  16. import org.jboss.netty.channel.ChannelHandlerContext;
  17. import org.jboss.netty.channel.MessageEvent;
  18. import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
  19. import org.jboss.netty.handler.codec.http.DefaultHttpResponse;
  20. import org.jboss.netty.handler.codec.http.HttpHeaders.Names;
  21. import org.jboss.netty.handler.codec.http.HttpRequest;
  22. import org.jboss.netty.handler.codec.http.HttpResponseStatus;
  23. import org.jboss.netty.handler.codec.http.HttpVersion;
  24. import org.jboss.netty.handler.codec.http.websocket.WebSocketFrame;
  25. import org.jboss.netty.util.CharsetUtil;
  26. import java.io.IOException;
  27. import java.util.Map;
  28. import java.util.concurrent.Callable;
  29. /**
  30. * @author dhanji@gmail.com (Dhanji R. Prasanna)
  31. */
  32. @Singleton
  33. class SitebricksAsyncHandler extends SimpleChannelUpstreamHandler {
  34. private static final Key<HttpRequest> HTTP_REQUEST_KEY = Key.get(HttpRequest.class);
  35. private final RoutingDispatcher dispatcher;
  36. private final Provider<Bootstrapper> bootstrapper;
  37. private final Provider<Shutdowner> teardowner;
  38. private final Provider<Request> requestProvider;
  39. private final NettyReplyMaker replyMaker;
  40. @Inject
  41. SitebricksAsyncHandler(RoutingDispatcher dispatcher, Provider<Bootstrapper> bootstrapper,
  42. Provider<Shutdowner> teardowner,
  43. Provider<Request> requestProvider,
  44. NettyReplyMaker replyMaker) {
  45. this.dispatcher = dispatcher;
  46. this.bootstrapper = bootstrapper;
  47. this.teardowner = teardowner;
  48. this.replyMaker = replyMaker;
  49. this.requestProvider = requestProvider;
  50. }
  51. private Config config;
  52. public void startup(Config config) {
  53. this.config = config;
  54. bootstrapper.get().start();
  55. }
  56. public void shutdown() {
  57. teardowner.get().shutdown();
  58. }
  59. @Override
  60. public void messageReceived(final ChannelHandlerContext ctx, MessageEvent e)
  61. throws Exception {
  62. Object message = e.getMessage();
  63. if (message instanceof HttpRequest) {
  64. // Handle normal request.
  65. HttpRequest request = (HttpRequest) message;
  66. // Scope this request using Guice thread-local scopes.
  67. Map<Key<?>,Object> seedMap = Maps.newHashMap();
  68. seedMap.put(HTTP_REQUEST_KEY, request);
  69. ServletScopes.scopeRequest(new Callable<Object>() {
  70. @Override
  71. public Object call() throws Exception {
  72. // Close channel once we're done with request.
  73. handleHttpRequest(ctx).addListener(new ChannelFutureListener() {
  74. @Override
  75. public void operationComplete(ChannelFuture future) throws Exception {
  76. try {
  77. dispatcher.dispatch(requestProvider.get(), Events.AFTER);
  78. } finally {
  79. ChannelFutureListener.CLOSE.operationComplete(future);
  80. }
  81. }
  82. });
  83. return null;
  84. }
  85. }, seedMap).call();
  86. } else if (message instanceof WebSocketFrame) {
  87. // Handle websocket frame.
  88. }
  89. }
  90. private ChannelFuture handleHttpRequest(ChannelHandlerContext ctx) throws IOException {
  91. // Because this method executes within a request scope, we can obtain the Sitebricks
  92. // request directly from its provider.
  93. Object respondObject = dispatcher.dispatch(this.requestProvider.get(), Events.DURING);
  94. //was there any matching page? (if it was a headless response, we don't need to do anything).
  95. // Also we do not do anything if the page elected to do nothing.
  96. if (null != respondObject) {
  97. // Only use the string rendering pipeline if this is not a headless request.
  98. if (respondObject instanceof Respond) {
  99. Respond respond = (Respond) respondObject;
  100. //do we need to redirect or was this a successful render?
  101. final String redirect = respond.getRedirect();
  102. if (null != redirect) {
  103. // A redirect is called for...
  104. DefaultHttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1,
  105. HttpResponseStatus.TEMPORARY_REDIRECT);
  106. response.setHeader(Names.LOCATION, redirect);
  107. return ctx.getChannel().write(response);
  108. } else { //successful render
  109. // by checking if a content type was set, we allow users to override content-type
  110. // on an arbitrary basis
  111. DefaultHttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1,
  112. HttpResponseStatus.OK);
  113. response.setContent(ChannelBuffers.copiedBuffer(respond.toString(), CharsetUtil.UTF_8));
  114. response.setHeader(Names.CONTENT_TYPE, respond.getContentType());
  115. // Apparently you only need this for a keepAlive connection? Handle them...
  116. response.setHeader(Names.CONTENT_LENGTH, response.getContent().readableBytes());
  117. return ctx.getChannel().write(response);
  118. }
  119. } else { // It must be a headless Reply. Render the headless response.
  120. assert respondObject instanceof Reply; // This just has to be true.
  121. return ctx.getChannel().write(replyMaker.populate((Reply<Object>) respondObject));
  122. }
  123. } else {
  124. // Resource not found, reject (TODO we should serve static resources here from the file system)
  125. DefaultHttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1,
  126. HttpResponseStatus.NOT_FOUND);
  127. System.out.println("Not found for " + requestProvider.get().path());
  128. return ctx.getChannel().write(response);
  129. }
  130. }
  131. }