PageRenderTime 41ms CodeModel.GetById 28ms app.highlight 10ms RepoModel.GetById 1ms app.codeStats 0ms

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