Permalink
Browse files

Make scalatra app scoped too

  • Loading branch information...
1 parent 925c795 commit 299791b8d511587a764fe1755669a344a07919e9 @casualjim casualjim committed Dec 17, 2012
@@ -0,0 +1,14 @@
+package org.scalatra
+
+import javax.servlet.http.{HttpServletResponse, HttpServletRequest}
+
+/**
+ * A `Handler` is the Scalatra abstraction for an object that operates on
+ * a request/response pair.
+ */
+trait Handler {
+ /**
+ * Handles a request and writes to the response.
+ */
+ def handle(request: HttpServletRequest, res: HttpServletResponse): Unit
+}
@@ -0,0 +1,19 @@
+package org.scalatra.servlet
+
+import org.scalatra.Handler
+import javax.servlet.ServletConfig
+import javax.servlet.http.{HttpServlet, HttpServletResponse, HttpServletRequest}
+
+final class ScalatraHost[T <: Handler](factory: (ServletConfig, HttpServletRequest, HttpServletResponse) => T) extends HttpServlet {
+
+ private[this] var conf: ServletConfig = _
+ override def init(config: ServletConfig) {
+ conf = config
+ super.init(config)
+ }
+
+ override def service(req: HttpServletRequest, res: HttpServletResponse) {
+ val app = factory(conf, req, res)
+ app.handle(req, res)
+ }
+}
@@ -2,11 +2,12 @@ package org.scalatra
import javax.servlet.ServletContext
import javax.servlet.http.{HttpServletRequest, HttpServletResponse}
+import servlet.ServletApiImplicits
/**
* The core Scalatra DSL.
*/
-trait CoreDsl extends Handler with Control {
+trait CoreDsl extends Handler with Control with ServletApiImplicits {
@deprecated("Use servletContext instead", "2.1.0")
def applicationContext: ServletContext = servletContext
@@ -1,15 +1,15 @@
-package org.scalatra
-
-import javax.servlet.http.{HttpServletRequest, HttpServletResponse}
-import servlet.ServletApiImplicits
-
-/**
- * A `Handler` is the Scalatra abstraction for an object that operates on
- * a request/response pair.
- */
-trait Handler extends ServletApiImplicits {
- /**
- * Handles a request and writes to the response.
- */
- def handle(request: HttpServletRequest, res: HttpServletResponse): Unit
-}
+//package org.scalatra
+//
+//import javax.servlet.http.{HttpServletRequest, HttpServletResponse}
+//import servlet.ServletApiImplicits
+//
+///**
+// * A `Handler` is the Scalatra abstraction for an object that operates on
+// * a request/response pair.
+// */
+//trait Handler extends ServletApiImplicits {
+// /**
+// * Handles a request and writes to the response.
+// */
+// def handle(request: HttpServletRequest, res: HttpServletResponse): Unit
+//}
@@ -1,8 +1,8 @@
package org.scalatra
import scala.collection.SortedSet
-import javax.servlet.http.{ HttpServletRequest, HttpServletRequestWrapper,
- HttpServletResponse }
+import javax.servlet.http.{HttpServletRequest, HttpServletRequestWrapper, HttpServletResponse}
+import servlet.ServletApiImplicits
object MethodOverride {
val ParamName = "_method"
@@ -16,8 +16,7 @@ object MethodOverride {
* If the request is a POST and the `_method` request parameter is set,
* the value of the `_method` parameter is treated as the request's method.
*/
-trait MethodOverride extends Handler {
-
+trait MethodOverride extends Handler with ServletApiImplicits {
abstract override def handle(req: HttpServletRequest, res: HttpServletResponse) {
val req2 = req.requestMethod match {
case Post => new HttpServletRequestWrapper(req) {
@@ -0,0 +1,178 @@
+package org.scalatra
+
+import javax.servlet.http.{HttpServletResponse, HttpServletRequest}
+import java.util.concurrent.atomic.AtomicReference
+import collection.immutable.DefaultMap
+import javax.servlet.ServletContext
+import java.{ util => ju }
+import collection.JavaConverters._
+
+abstract class ScalatraApp[T <: {
+ def getServletContext(): ServletContext
+ def getInitParameter(name: String): String
+ def getInitParameterNames(): ju.Enumeration[String]
+ }](override val config: T, req: HttpServletRequest, res: HttpServletResponse) extends ScalatraSyntax with RequestResponseScope with Initializable {
+
+ type ConfigT = T
+ private[this] val _request = new AtomicReference[HttpServletRequest](req)
+ private[this] val _response = new AtomicReference[HttpServletResponse](res)
+
+ protected def withRequestResponse[A](request: HttpServletRequest, response: HttpServletResponse)(f: => A): A = {
+ val oldReq = this.request
+ val oldRes = this.response
+ val resetReq = _request.compareAndSet(oldReq, request)
+ val resetRes = _response.compareAndSet(oldRes, response)
+ val res = f
+ if (resetRes) _response.compareAndSet(response, oldRes)
+ if (resetReq) _request.compareAndSet(request, oldReq)
+ res
+ }
+
+ /**
+ * Executes the block with the given request bound to the `request`
+ * method.
+ */
+ protected def withRequest[A](request: HttpServletRequest)(f: => A): A = {
+ val oldReq = this.request
+ val reset = _request.compareAndSet(oldReq, request)
+ val res = f
+ if (reset) _request.compareAndSet(request, oldReq)
+ res
+ }
+
+ /**
+ * Executes the block with the given response bound to the `response`
+ * method.
+ */
+ protected def withResponse[A](response: HttpServletResponse)(f: => A): A = {
+ val oldRes = this.response
+ _response.set(response)
+ val res = f
+ _response.set(oldRes)
+ res
+ }
+
+
+ /**
+ * The currently scoped request. Valid only inside the `handle` method.
+ */
+ implicit def request: HttpServletRequest = _request.get()
+
+ /**
+ * The currently scoped response. Valid only inside the `handle` method.
+ */
+ implicit def response: HttpServletResponse = _response.get()
+
+ @deprecated("Move this code into the constructor, the initialize method is not really used in a scalatra app.", "2.2")
+ def initialize(config: ConfigT) {}
+ initialize(config)
+
+ /**
+ * Defines the request path to be matched by routers. The default
+ * definition is optimized for `path mapped` servlets (i.e., servlet
+ * mapping ends in `&#47;*`). The route should match everything matched by
+ * the `&#47;*`. In the event that the request URI equals the servlet path
+ * with no trailing slash (e.g., mapping = `/admin&#47;*`, request URI =
+ * '/admin'), a '/' is returned.
+ *
+ * All other servlet mappings likely want to return request.getServletPath.
+ * Custom implementations are allowed for unusual cases.
+ */
+ def requestPath = {
+ def getRequestPath = request.getRequestURI match {
+ case requestURI: String =>
+ var uri = requestURI
+ if (request.getContextPath != null && request.getContextPath.trim.nonEmpty) uri = uri.substring(request.getContextPath.length)
+ if (request.getServletPath != null && request.getServletPath.trim.nonEmpty) uri = uri.substring(request.getServletPath.length)
+ if (uri.isEmpty) {
+ uri = "/"
+ } else {
+ val pos = uri.indexOf(';')
+ if (pos >= 0) uri = uri.substring(0, pos)
+ }
+ UriDecoder.firstStep(uri)
+ case null => "/"
+ }
+
+ request.get("org.scalatra.ScalatraServlet.requestPath") match {
+ case Some(uri) => uri.toString
+ case _ => {
+ val requestPath = getRequestPath
+ request.setAttribute("org.scalatra.ScalatraServlet.requestPath", requestPath)
+ requestPath.toString
+ }
+ }
+ }
+
+ protected def routeBasePath = {
+ if (request == null)
+ throw new IllegalStateException("routeBasePath requires an active request to determine the servlet path")
+ request.getContextPath + request.getServletPath
+ }
+
+ /**
+ * Invoked when no route matches. By default, calls `serveStaticResource()`,
+ * and if that fails, calls `resourceNotFound()`.
+ *
+ * This action can be overridden by a notFound block.
+ */
+ protected var doNotFound: Action = () => {
+ serveStaticResource() getOrElse resourceNotFound()
+ }
+
+ /**
+ * Attempts to find a static resource matching the request path. Override
+ * to return None to stop this.
+ */
+ protected def serveStaticResource(): Option[Any] =
+ servletContext.resource(request) map { _ =>
+ servletContext.getNamedDispatcher("default").forward(request, response)
+ }
+
+ /**
+ * Called by default notFound if no routes matched and no static resource
+ * could be found.
+ */
+ protected def resourceNotFound(): Any = {
+ response.setStatus(404)
+ if (isDevelopmentMode) {
+ val error = "Requesting \"%s %s\" on servlet \"%s\" but only have: %s"
+ response.getWriter println error.format(
+ request.getMethod,
+ Option(request.getPathInfo) getOrElse "/",
+ request.getServletPath,
+ routes.entryPoints.mkString("<ul><li>", "</li><li>", "</li></ul>"))
+ }
+ }
+
+ protected implicit def configWrapper(config: ConfigT) = new Config {
+ def context = config.getServletContext
+
+ object initParameters extends DefaultMap[String, String] {
+ def get(key: String): Option[String] = Option(config.getInitParameter(key))
+
+ def iterator: Iterator[(String, String)] = new Iterator[(String, String)] {
+ private val b = config.getInitParameterNames()
+ def hasNext: Boolean = b.hasMoreElements
+
+ def next(): (String, String) = {
+ val name = b.nextElement()
+ (name, config.getInitParameter(name))
+ }
+ }
+ }
+ }
+
+
+ def addSessionId(uri: String) = response.encodeURL(uri)
+
+ override def handle(request: HttpServletRequest, response: HttpServletResponse) {
+ // As default, the servlet tries to decode params with ISO_8859-1.
+ // It causes an EOFException if params are actually encoded with the
+ // other code (such as UTF-8)
+ if (request.getCharacterEncoding == null)
+ request.setCharacterEncoding(defaultCharacterEncoding)
+ super.handle(request, response)
+ }
+}
+
@@ -1,6 +1,7 @@
package org.scalatra
import scala.util.matching.Regex
+import servlet.ServletApiImplicits
import util.conversion.DefaultImplicitConversions
import util.io.zeroCopy
import java.io.{File, FileInputStream}
@@ -40,7 +41,7 @@ object ScalatraBase {
* The base implementation of the Scalatra DSL. Intended to be portable
* to all supported backends.
*/
-trait ScalatraBase extends CoreDsl with DynamicScope with Initializable with ScalatraParamsImplicits with DefaultImplicitConversions {
+trait ScalatraSyntax extends CoreDsl with RequestResponseScope with Initializable with ServletApiImplicits with ScalatraParamsImplicits with DefaultImplicitConversions {
import ScalatraBase.{HostNameKey, PortKey, ForceHttpsKey}
/**
* The routes registered in this kernel.
@@ -446,16 +447,9 @@ trait ScalatraBase extends CoreDsl with DynamicScope with Initializable with Sca
/**
* The configuration, typically a ServletConfig or FilterConfig.
*/
- private[this] var config: Config = _
+ protected def config: ConfigT
- /**
- * Initializes the kernel. Used to provide context that is unavailable
- * when the instance is constructed, for example the servlet lifecycle.
- * Should set the `config` variable to the parameter.
- *
- * @param config the configuration.
- */
- def initialize(config: ConfigT) { this.config = config }
+ def initialize(config: ConfigT)
/**
* Gets an init paramter from the config.
@@ -612,4 +606,21 @@ trait ScalatraBase extends CoreDsl with DynamicScope with Initializable with Sca
* The base implementation of the Scalatra DSL. Intended to be portable
* to all supported backends.
*/
-trait ScalatraBase extends ScalatraSyntax with DynamicScope
+trait ScalatraBase extends ScalatraSyntax with DynamicScope {
+
+
+ /**
+ * The configuration, typically a ServletConfig or FilterConfig.
+ */
+ protected var config: ConfigT = _
+
+ /**
+ * Initializes the kernel. Used to provide context that is unavailable
+ * when the instance is constructed, for example the servlet lifecycle.
+ * Should set the `config` variable to the parameter.
+ *
+ * @param config the configuration.
+ */
+ def initialize(config: ConfigT) { this.config = config }
+
+}
@@ -21,6 +21,7 @@ import javax.servlet._
*
* @see ScalatraServlet
*/
+@deprecated("This aproach uses thread locals which cause headaches in a multi-threading scenario, use org.scalatra.ScalatraApp instead.", "2.2")
trait ScalatraFilter extends Filter with ServletBase {
private[this] val _filterChain = new DynamicVariable[FilterChain](null)
protected def filterChain = _filterChain.value
@@ -1,40 +0,0 @@
-package org.scalatra
-
-import javax.servlet.http.{HttpServlet, HttpServletResponse, HttpServletRequest}
-import javax.servlet.{ServletConfig, ServletResponse, ServletRequest}
-
-trait AppReqResScope extends RequestResponseScope {
-
- protected def withRequestResponse[A](request: HttpServletRequest, response: HttpServletResponse)(f: => A): A = f
-
- /**
- * Executes the block with the given request bound to the `request`
- * method.
- */
- protected def withRequest[A](request: HttpServletRequest)(f: => A): A = f
-
- /**
- * Executes the block with the given response bound to the `response`
- * method.
- */
- protected def withResponse[A](response: HttpServletResponse)(f: => A): A = f
-}
-
-abstract class ScalatraApp(implicit val request: HttpServletRequest, implicit val response: HttpServletResponse) extends ScalatraSyntax with AppReqResScope with Initializable {
-
-}
-
-class ScalatraHost(factory: (HttpServletRequest, HttpServletResponse) => ScalatraApp) extends HttpServlet {
-
- private[this] var conf: ServletConfig = _
- override def init(config: ServletConfig) {
- conf = config
- super.init(config)
- }
-
- override def service(req: HttpServletRequest, res: HttpServletResponse) {
- val app = factory(req, res)
- app.initialize(conf.asInstanceOf[app.ConfigT])
- app.handle(req, res)
- }
-}
@@ -47,6 +47,7 @@ object ScalatraServlet {
*
* @see ScalatraFilter
*/
+@deprecated("This aproach uses thread locals which cause headaches in a multi-threading scenario, use org.scalatra.ScalatraApp instead.", "2.2")
abstract class ScalatraServlet
extends HttpServlet
with ServletBase
Oops, something went wrong.

0 comments on commit 299791b

Please sign in to comment.