Created
December 7, 2011 22:57
-
-
Save joegaudet/1445163 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.matygo.flow | |
import java.io.File | |
import java.io.PrintWriter | |
import java.io.StringWriter | |
import scala.collection.mutable.Map | |
import scala.util.DynamicVariable | |
import org.scalatra.ScalatraKernel.Action | |
import org.scalatra.RouteMatcher | |
import org.scalatra.ScalatraKernel | |
import org.scalatra.ContentTypeInferrer | |
import org.squeryl.PrimitiveTypeMode.transaction | |
import com.matygo.csv.CSVBuilder | |
import com.matygo.responses.Destroyed.DestroyResponse | |
import com.matygo.models.system.MatygoFile | |
import com.matygo.models.SproutcoreModel | |
import com.matygo.util.MLog | |
import javax.servlet.http.HttpServletRequest | |
import javax.servlet.http.HttpServletResponse | |
import net.liftweb.json.JsonAST.JValue | |
import net.liftweb.json.JsonDSL.pair2jvalue | |
import net.liftweb.json.Printer.compact | |
import net.liftweb.json.JsonAST | |
import com.matygo.responses.Destroyed | |
import com.matygo.implicits.MatygoImplicits | |
import net.liftweb.json.JsonAST.JObject | |
import net.liftweb.json.JsonDSL.seq2jvalue | |
import com.matygo.util.MLogFunctions | |
trait FlowController extends ScalatraKernel with FlashSupport with MatygoImplicits with MLogFunctions{ | |
def routeBasePath = "/" | |
/** dispatcher, to register paths accepted by this controller **/ | |
val dispatcher: FlowDispatcher | |
/** Wrap Request **/ | |
protected[flow] val reqDV = new DynamicVariable[FlowRequest](null) | |
protected def req: FlowRequest = reqDV.value | |
/** Wrap Request **/ | |
protected[flow] val bindingsDV = new DynamicVariable[Map[String, Any]](null) | |
protected def bindings: Map[String, Any] = bindingsDV.value | |
/** profiling **/ | |
class Profiler(request: HttpServletRequest, controller: FlowController) { | |
var startTime: Long = System.currentTimeMillis | |
var stopTime: Long = System.currentTimeMillis | |
var size: Long = 0 | |
var uri: String = request.getRequestURI | |
var method: String = request.getMethod | |
var user = "" | |
def start = startTime = System.currentTimeMillis | |
def stop = stopTime = System.currentTimeMillis | |
def took = stopTime - startTime | |
def bytes = if (size > 0) size + " bytes" else "" | |
try { | |
val currentUser = transaction(FlowAuthentication.userFromRequest(request, response)) | |
currentUser match { | |
case Some(u) => { | |
user = u.id.toString | |
} | |
case None => None | |
} | |
} | |
override def toString(): String = { | |
"[%, %, % ms, %, %, %]" % (method, uri, took, bytes, controller.getClass.getName, user) | |
} | |
} | |
protected[flow] val profileDV = new DynamicVariable[Profiler](null) | |
protected def profile: Profiler = profileDV.value | |
// Scalatra Kernel Overrides | |
def kernelName = this.toString | |
def servletContext = dispatcher.servletContext | |
protected var doNotFound: Action = () => { | |
response.setStatus(404) | |
response.getWriter println "Requesting %s but only have %s".format(request.getRequestURI, routes) | |
} | |
def requestPath = if (dispatcher.request.getPathInfo != null) dispatcher.request.getPathInfo else dispatcher.request.getServletPath | |
// Result rendering | |
def json2String(json: JValue) = compact(JsonAST.render(("content" -> json))) | |
override protected def contentTypeInferrer: ContentTypeInferrer = { | |
case f: MatygoFile => { | |
println("Inferring content type for a file!!!!") | |
f.inferredContentType | |
} | |
case t: Traversable[_] => t.headOption match { | |
case Some(element) => if (element.isInstanceOf[JObject] || element.isInstanceOf[SproutcoreModel]) "application/json" else "text/html" | |
case None => "text/html" | |
} | |
case _: JValue => "application/json" | |
case _: SproutcoreModel => "application/json" | |
case _: Render => "text/html" | |
case _: DestroyResponse => "application/json" | |
case _: CSVBuilder => "text/csv" | |
case _: String => "text/plain" | |
case _: Array[Byte] => "application/octet-stream" | |
case _ => "text/html" | |
} | |
override protected def renderResponseBody(actionResult: Any) { | |
actionResult match { | |
case json: JValue => renderResponseBody(json2String(json)) | |
case model: SproutcoreModel => renderResponseBody(json2String(transaction(model.toJSON(None)))) | |
case render: Render => renderResponseBody(render.doTemplating) | |
case destroyed: DestroyResponse => renderResponseBody(Destroyed(destroyed)) | |
case csv: CSVBuilder => renderResponseBody(transaction(csv.toCSVString)) | |
case f: MatygoFile => renderResponse(f.fileOnDisk) | |
case _ => { | |
profile.size = actionResult match { | |
case array: Array[_] => array.size | |
case f: File => f.length | |
case str: String => str.length | |
case _ => 0 | |
} | |
super.renderResponseBody(actionResult) | |
} | |
} | |
} | |
override def handle(request: HttpServletRequest, response: HttpServletResponse) { | |
profileDV.withValue(new Profiler(request, this)) { | |
flashDV.withValue(getFlash(request)) { | |
bindingsDV.withValue(Map("error" -> "", "warning" -> "", "info" -> "")) { | |
reqDV.withValue(new FlowRequest(request)) { | |
try { | |
super.handle(request, response) | |
request.getSession.setAttribute(FlashSupport.sessionKey, flash) | |
flash.sweep() | |
} catch { | |
case e => { | |
val (status, message) = ExceptionResponse(e) | |
MLog.logger.info("Handled invalid action, sending % : %" % (status, json2String(message))) | |
// uncomment to see track trace during testing` | |
//e.printStackTrace() | |
// log exception | |
import java.io.{ StringWriter, PrintWriter } | |
val writer = new StringWriter | |
e.printStackTrace(new PrintWriter(writer)) | |
MLog.logger.warning(writer.toString()) | |
response.setStatus(status) | |
response.getWriter.print(json2String(message)) | |
} | |
} | |
} | |
} | |
} | |
profile.stop | |
MLog.logger.info(profile.toString) | |
} | |
} | |
/** Render methods **/ | |
def render(template: String, bindingsA: (String, Any)*) = { | |
for ((k, v) <- flash) bindings(k) = v | |
Render(template, params, bindings, bindingsA: _*) | |
} | |
/** | |
* Redefining Get/Post/Put/Delete methods to wrap in transaction and register routes with dispatcher | |
* | |
*/ | |
/** | |
* @see [[org.scalatra.ScalatraKernel.get]] | |
*/ | |
override def get(routeMatchers: RouteMatcher*)(action: => Any) = { | |
dispatcher.addGetRoute(routeMatchers, this) | |
super.get(routeMatchers: _*)({ | |
transaction(action) | |
}) | |
} | |
/** | |
* @see [[org.scalatra.ScalatraKernel.post]] | |
*/ | |
override def post(routeMatchers: RouteMatcher*)(action: => Any) = { | |
dispatcher.addPostRoute(routeMatchers, this) | |
super.post(routeMatchers: _*)({ | |
transaction(action) | |
}) | |
} | |
/** | |
* @see [[org.scalatra.ScalatraKernel.put]] | |
*/ | |
override def put(routeMatchers: RouteMatcher*)(action: => Any) = { | |
dispatcher.addPutRoute(routeMatchers, this) | |
super.put(routeMatchers: _*)({ | |
transaction(action) | |
}) | |
} | |
/** | |
* @see [[org.scalatra.ScalatraKernel.delete]] | |
*/ | |
override def delete(routeMatchers: RouteMatcher*)(action: => Any) = { | |
dispatcher.addDeleteRoute(routeMatchers, this) | |
super.delete(routeMatchers: _*)({ | |
transaction(action) | |
}) | |
} | |
/** | |
* @see [[org.scalatra.ScalatraKernel.options]] | |
*/ | |
override def options(routeMatchers: RouteMatcher*)(action: => Any) = { | |
dispatcher.addOptionsRoute(routeMatchers, this) | |
super.options(routeMatchers: _*)({ | |
transaction(action) | |
}) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment