casualjim
1/18/2012 - 8:38 PM

gistfile1.scala

package com.matygo.controllers
import java.io.File
import java.io.PrintWriter
import java.io.StringWriter
import scala.collection.JavaConversions.mapAsScalaMap
import org.scalatra.ScalatraKernel.MultiParamsKey
import org.scalatra.ContentTypeInferrer
import org.scalatra.util.MultiMap
import org.scalatra.CookieSupport
import org.scalatra.RouteMatcher
import org.scalatra.ScalatraFilter
import org.squeryl.PrimitiveTypeMode.transaction
import com.matygo.csv.CSVBuilder
import com.matygo.exceptions.NotLoggedInException
import com.matygo.flow.ExceptionResponse
import com.matygo.flow.FlowRequest
import com.matygo.flow.Render
import com.matygo.implicits.MatygoImplicits
import com.matygo.models.system.MatygoFile
import com.matygo.models.users.User
import com.matygo.models.SproutcoreModel
import com.matygo.responses.Destroyed.DestroyResponse
import com.matygo.responses.Destroyed
import com.matygo.util.MLogFunctions
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse
import net.liftweb.json.JsonAST.JObject
import net.liftweb.json.JsonAST.JValue
import net.liftweb.json.JsonDSL._
import net.liftweb.json.Printer.compact
import net.liftweb.json.JsonAST
import com.matygo.models.Environment
import com.matygo.models.Environments
import net.liftweb.json.JsonAST.JString
import net.liftweb.json.JsonAST.JArray
import com.matygo.util.SQLUtils
import org.scalatra.FlashMapSupport
import com.matygo.exceptions.NotAuthorizedException
import com.matygo.exceptions.NotFoundException
import org.scalatra.SweetCookies
import org.scalatra.CookieOptions

class MatygoController extends ScalatraFilter with CookieSupport with MatygoImplicits with MLogFunctions with MatygoAuthentication with SQLUtils with FlashMapSupport {

    override def routeBasePath = "/"

    def json2String(json: JValue) = compact(JsonAST.render(("content" -> json)))

    protected def currentUser: Option[User] = {
        val user = userFromRequest
        if (!(renderLoggedOutContent || allowsLoggedOutContent) && user.isEmpty) throw new NotLoggedInException
        user
    }

    // bindings
    protected def bindings = request.getOrElseUpdate("bindings", scala.collection.mutable.Map()).asInstanceOf[scala.collection.mutable.Map[String, Any]]

    private def renderLoggedOutContent = request.get("rendersLoggedOutContent") match {
        case Some(_) => true
        case None    => false
    }

    def rendersLoggedOutContent = {
        request("rendersLoggedOutContent") = Boolean.box(true)
    }

    def allowsLoggedOutContent = false

    // Scalatra Kernel Overrides
    def kernelName = this.toString

    //    def servletContext = dispatcher.servletContext
    //    protected override def 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

    /** Render methods **/
    def render(template: String, bindingsA: (String, Any)*) = {
        for ((k, v) <- flash) bindings(k) = v
        Render(template, params, bindings, bindingsA: _*)
    }

    private def ourContentTypeInferrer: ContentTypeInferrer = {
      case f: MatygoFile => 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 contentTypeInferrer: ContentTypeInferrer = ourContentTypeInferrer orElse super.contentTypeInferrer

    private def ourRenderPipeline: PartialFunction[Any, Any] = {
        case json: JValue => json2String(json)
        case t: Traversable[_] => t.headOption match {
            case Some(element) => {
                rseq2jvalue(element match {
                    case model: SproutcoreModel => {
                        val ct = t.asInstanceOf[Traversable[SproutcoreModel]]
                        ct.map(_.toJSON(currentUser))
                    }
                    case json: JValue => {
                        t.asInstanceOf[Traversable[JValue]]
                    }
                    case a: Any => {
                        t.map(e => JString(e.toString()))
                    }
                })
            }
            case None => JArray(Nil)
        }
        case model: SproutcoreModel     => transaction(model.toJSON(currentUser))
        case render: Render             => render.doTemplating
        case destroyed: DestroyResponse => Destroyed(destroyed)
        case csv: CSVBuilder            => transaction(csv.toCSVString)
        case f: MatygoFile              => f.fileOnDisk
    }

    override protected def renderPipeline = ourRenderPipeline orElse super.renderPipeline

    override protected def renderResponseBody(actionResult: Any) = {
        request("contentWritten") = Long.box(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) {
        _request.withValue(request) {
            _response.withValue(response) {
                transaction {
                        request("start") = Long.box(System.currentTimeMillis)
                        super.handle(request, response)
                        info(reportProfile)
                        
                }
            }
        }
    }

    error {
      case e => {
            val (theStatus, message) = ExceptionResponse(e)
            val json = json2String(message)
            info("Handled invalid action, sending % : %" % (status, json))

            import java.io.{ StringWriter, PrintWriter }

            e match {
                case _: NotLoggedInException   => ()
                case _: NotAuthorizedException => ()
                case _: NotFoundException => {
                    val stackTrace = e.getStackTrace()
                    val writer = new StringWriter
                    for (i <- 0 to 5) {
                        writer.append(stackTrace(i).toString())
                        writer.append("\n")
                    }
                    warning(writer.toString())
                }
                case _ => {
                    // log exception if it's not a loggedin one since those happen a lot
                    val writer = new StringWriter
                    e.printStackTrace(new PrintWriter(writer))
                    error(writer.toString())

                    // if we are in testing mode lets
                    // shit out the stack trace
                    if (Environment.mode == Environments.Testing)
                        e.printStackTrace()
                }
            }

            status = theStatus
            message
        }
                    
    }

    /** Render methods **/
    //    def render(template: String, bindingsA: (String, Any)*) = {
    //        for ((k, v) <- flash) bindings(k) = v
    //        Render(template, params, bindings, bindingsA: _*)
    //    }

    def req = new FlowRequest(request)

    /**
     * Redefining Get/Post/Put/Delete methods to wrap in transaction and register routes with dispatcher
     *
     */

    def reportProfile = {
        val took = System.currentTimeMillis() - request.getOrElse("start", 0).asInstanceOf[Long]
        "[%, %, % ms, %, %, %]" % (request.getMethod, request.getRequestURI, took, request("contentWritten"), getClass.getName, transaction(userFromRequest.map(_.id).getOrElse("")))
    }

}