Skip to content

Instantly share code, notes, and snippets.

@joegaudet
Created December 7, 2011 22:57
Show Gist options
  • Save joegaudet/1445163 to your computer and use it in GitHub Desktop.
Save joegaudet/1445163 to your computer and use it in GitHub Desktop.
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