package com.google.sitebricks; import com.google.inject.Inject; import com.google.inject.Singleton; import com.google.sitebricks.compiler.TemplateCompileException; import com.google.sitebricks.headless.Request; import com.google.sitebricks.routing.PageBook; import com.google.sitebricks.routing.Production; import com.google.sitebricks.routing.RoutingDispatcher; import com.google.sitebricks.routing.SystemMetrics; import net.jcip.annotations.ThreadSafe; import org.mvel2.PropertyAccessException; import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; import java.lang.reflect.InvocationTargetException; /** * In debug mode, this dispatcher is used to intercept the production dispatcher and provide debug * services (such as the /debug page, and the friendly compile errors page). * * @author Dhanji R. Prasanna (dhanji@gmail com) */ @ThreadSafe @Singleton class DebugModeRoutingDispatcher implements RoutingDispatcher { private final RoutingDispatcher dispatcher; private final SystemMetrics metrics; private final PageBook pageBook; @Inject public DebugModeRoutingDispatcher(@Production RoutingDispatcher dispatcher, SystemMetrics metrics, PageBook pageBook) { this.dispatcher = dispatcher; this.metrics = metrics; this.pageBook = pageBook; } public Object dispatch(Request request, Events event) throws IOException { long start = System.currentTimeMillis(); // Attempt to discover page class. final PageBook.Page page = pageBook.get(request.path()); // This may be a static resource (in which case we dont gather metrics for it). Class<?> pageClass = null; if (null != page) pageClass = page.pageClass(); try { return dispatcher.dispatch(request, Events.DURING); } catch (TemplateCompileException tce) { // NOTE(dhanji): Don't log error metrics here, they are better handled by the compiler. final Respond respond = new StringBuilderRespond(new Object()); respond.write("<h3>"); respond.write("Compile errors in page"); respond.write("</h3>"); respond.write("<pre>"); respond.write(tce.getMessage()); respond.write("</pre>"); respond.write("<br/>"); respond.write("<br/>"); respond.write("<br/>"); return respond; } catch (PropertyAccessException pae) { final Respond respond = new StringBuilderRespond(new Object()); Throwable cause = pae.getCause(); respond.write("<h3>"); respond.write("Exception during page render"); respond.write("</h3>"); respond.write("<br/>"); respond.write("<br/>"); respond.write("<br/>"); // Analyze cause and construct a detailed error report. if (cause instanceof InvocationTargetException) { InvocationTargetException ite = (InvocationTargetException) cause; cause = ite.getCause(); } if (cause == null) cause = pae; // Create ourselves a printwriter to buffer error output into. final StringWriter writer = new StringWriter(); cause.printStackTrace(new PrintWriter(writer)); respond.write("<h3>"); respond.write("Exception during page render"); respond.write("</h3>"); respond.write("<pre>"); respond.write(writer.toString()); respond.write("</pre>"); return respond; } finally { long time = System.currentTimeMillis() - start; // Only log time metric if this is a dynamic resource. if (null != pageClass) metrics.logPageRenderTime(pageClass, time); } } }