/sitebricks/src/main/java/com/google/sitebricks/routing/WidgetRoutingDispatcher.java

http://github.com/dhanji/sitebricks · Java · 178 lines · 122 code · 29 blank · 27 comment · 17 complexity · 29080f0d9957d2d020a34d410cc01a21 MD5 · raw file

  1. package com.google.sitebricks.routing;
  2. import java.io.IOException;
  3. import java.util.List;
  4. import java.util.Set;
  5. import javax.servlet.http.HttpServletRequest;
  6. import javax.validation.ConstraintViolation;
  7. import javax.validation.ConstraintViolationException;
  8. import javax.validation.ValidationException;
  9. import net.jcip.annotations.Immutable;
  10. import com.google.inject.Inject;
  11. import com.google.inject.Provider;
  12. import com.google.inject.Singleton;
  13. import com.google.sitebricks.Respond;
  14. import com.google.sitebricks.StringBuilderRespond;
  15. import com.google.sitebricks.binding.FlashCache;
  16. import com.google.sitebricks.binding.RequestBinder;
  17. import com.google.sitebricks.client.transport.Json;
  18. import com.google.sitebricks.conversion.ValidationConverter;
  19. import com.google.sitebricks.headless.HeadlessRenderer;
  20. import com.google.sitebricks.headless.Reply;
  21. import com.google.sitebricks.headless.Request;
  22. import com.google.sitebricks.rendering.resource.ResourcesService;
  23. import com.google.sitebricks.routing.PageBook.Page;
  24. /**
  25. * @author Dhanji R. Prasanna (dhanji@gmail.com)
  26. */
  27. @Immutable
  28. @Singleton
  29. class WidgetRoutingDispatcher implements RoutingDispatcher {
  30. private final PageBook book;
  31. private final RequestBinder binder;
  32. private final ResourcesService resourcesService;
  33. private final Provider<FlashCache> flashCacheProvider;
  34. private final HeadlessRenderer headlessRenderer;
  35. @Inject
  36. Provider<HttpServletRequest> httpServletRequestProvider;
  37. @Inject
  38. private ValidationConverter validationConvertor;
  39. @Inject
  40. public WidgetRoutingDispatcher(PageBook book, RequestBinder binder,
  41. ResourcesService resourcesService,
  42. Provider<FlashCache> flashCacheProvider,
  43. HeadlessRenderer headlessRenderer) {
  44. this.headlessRenderer = headlessRenderer;
  45. this.book = book;
  46. this.binder = binder;
  47. this.resourcesService = resourcesService;
  48. this.flashCacheProvider = flashCacheProvider;
  49. }
  50. public Object dispatch(Request request, Events event) throws IOException {
  51. String uri = request.path();
  52. //first try dispatching as a static resource service
  53. Respond respond = resourcesService.serve(uri);
  54. if (null != respond)
  55. return respond;
  56. // Otherwise try to dispatch as a widget/page
  57. // Check if there is a page chain link sitting here
  58. // for this page.
  59. // NOTE(dhanji): we must use remove, to atomically
  60. // remove the page and process it in one go. It is
  61. // also worth coordinating this with conversation request
  62. // queueing.
  63. // TODO(dhanji): Change flashcache to use temporary cookies instead.
  64. PageBook.Page page = flashCacheProvider.get().remove(uri);
  65. // If there is no link, obtain page via Guice as normal.
  66. if (null == page)
  67. page = book.get(uri);
  68. //could not dispatch as there was no match
  69. if (null == page)
  70. return null;
  71. final Object instance = page.instantiate();
  72. if (page.isHeadless()) {
  73. return bindAndReply(request, page, instance);
  74. } else {
  75. //fire events and render responder
  76. return bindAndRespond(request, page, instance);
  77. }
  78. }
  79. private Object bindAndReply(Request request, Page page, Object instance) throws IOException {
  80. // bind request (sets request params, etc).
  81. binder.bind(request, instance);
  82. Object response = null;
  83. try {
  84. // call the appropriate handler.
  85. response = fireEvent(request, page, instance);
  86. }
  87. catch (ValidationException ve) {
  88. ConstraintViolationException cve = (ConstraintViolationException) ve.getCause();
  89. Set<? extends ConstraintViolation<?>> scv = (Set<? extends ConstraintViolation<?>>) cve.getConstraintViolations();
  90. List<String> errors = validationConvertor.to(scv);
  91. response = Reply.with(errors).as(Json.class).badRequest();
  92. }
  93. return response;
  94. }
  95. private Object bindAndRespond(Request request, PageBook.Page page, Object instance)
  96. throws IOException {
  97. //bind request
  98. binder.bind(request, instance);
  99. // fire get/post events
  100. Object redirect = null;
  101. List<String> errors = null;
  102. try {
  103. redirect = fireEvent(request, page, instance);
  104. }
  105. catch (ValidationException ve) {
  106. ve.getCause().printStackTrace();
  107. ConstraintViolationException cve = (ConstraintViolationException) ve.getCause();
  108. Set<? extends ConstraintViolation<?>> scv = (Set<? extends ConstraintViolation<?>>) cve.getConstraintViolations();
  109. errors = validationConvertor.to(scv);
  110. }
  111. //render to respond
  112. Respond respond = new StringBuilderRespond(instance);
  113. respond.setErrors(errors);
  114. if (null != redirect) {
  115. if (redirect instanceof String)
  116. respond.redirect((String) redirect);
  117. else if (redirect instanceof Class) {
  118. PageBook.Page targetPage = book.forClass((Class<?>) redirect);
  119. // should never be null coz it is validated on compile.
  120. respond.redirect(contextualize(request, targetPage.getUri()));
  121. } else if (redirect instanceof Reply<?>) {
  122. // To allow non-headless pages to use Reply<?> for more advanced HTTP responses
  123. return redirect;
  124. } else {
  125. // Handle page-chaining driven redirection.
  126. PageBook.Page targetPage = book.forInstance(redirect);
  127. // should never be null coz it will be validated at compile time.
  128. flashCacheProvider.get().put(targetPage.getUri(), targetPage);
  129. // Send to the canonical address of the page. This is also
  130. // verified at compile, not be a variablized matcher.
  131. respond.redirect(contextualize(request, targetPage.getUri()));
  132. }
  133. } else {
  134. page.widget().render(instance, respond);
  135. }
  136. return respond;
  137. }
  138. // We're sure the request parameter map is a Map<String, String[]>
  139. @SuppressWarnings("unchecked")
  140. private Object fireEvent(Request request, PageBook.Page page, Object instance)
  141. throws IOException {
  142. final String method = request.method();
  143. final String pathInfo = request.path();
  144. return page.doMethod(method.toLowerCase(), instance, pathInfo, request);
  145. }
  146. private static String contextualize(Request request, String targetUri) {
  147. return request.context() + targetUri;
  148. }
  149. }