@seratch seratch released this Nov 18, 2015 · 261 commits to master since this release

Assets 6

Skinny Framework 2.0.0 is out.

Topics

Replace Scalatra with skinny-micro

Skinny 2 has its own Servlet wrapper implementation. Skinny 2 won't be compatible with any versions of Scalatra. Skinny 1.x will keep compatible with Scalatra 2.3.x.

In 2.0.0 milestone releaes, the new implmenetation was a part of this project and named skinny-engine. After that, skinny-engine has been moved to an individual project named skinny-micro and many improvements are done.

https://github.com/skinny-framework/skinny-micro

Mostly DSLs are compatible but many improvements and minor API changes, renaming modules/classes and re-packaging have been applied. If you explicitly import org.scalatra package things into your applications, fixing packages or class/module names will be needed. Migration guide for details will come later.

  • SkinnyMicroServlet instead of ScalatraServlet
  • SkinnyMicroFilter instead of ScalatraFilter

Skinny 2 has its own forked scalatra-test named skinny-micro-test. If you're using ScalaTest, switching to skinny-micro-test will be so easy. Good to hear, your existing tests written with scalatra-test will work fine with Skinny 2 apps.

A solution for Servlet request reuse issue

When Servlet applications are running in async supported mode, Servlet containers sometimes re-use their HttpServletRequest objects in order to avoid the performance overhead of request object creation.

skinny-micro wraps HttpServletRequest like this.

skinny/micro/request/StableHttpServletRequest.scala

StableHttpServletRequest basically works for you even when the original request has been recycled. Actually, it's difficult to make all the APIs stable. However, our StableHttpServletRequest makes basic metadata of the request, request attributes and request headers always stable. We believe that will be enough.

A solution for accessing DynamicScope from other threads

Scalatra was originally designed to provide Sinatra-ish APIs for simple Servlet use cases. Traditionally Servlet apps allow thread-local approarch as same as other Java standard things.

Scalatra's DynamicScope can be a cause of trouble especially when you're dealing with Future values. For example, this code is very fragile because of accessing other thread's variable from other threads.

class Sample extends ScalatraServlet with FutureSupport {
  get("/") {
     new AsyncResult {
       override val is = {
         // the "request" is a thread-local value of the main thread which is came from the above DynamicScope
         // NullPointerException occasionally happens here
         request.getContextPath 
       }
     }
  }
}

Scala 2.10 introduced Future APIs after successful Twitter's its own Future APIs and Finagle library. Thinking back now, that was a major event for Scala eco-system. Recently so many libraries and coding practices expect seamlessly using Future everywhere that the DynamicScope issue is no longer a small issue now.

The root cause of the issue is that we can easily (or unexpectedly) access the main-thread's thread-local variable via implicit conversions everywhere and we cannot disable it in some way.

So skinny-micro is going to solve the issue by following approarch:

  • By adding implicit skinny.micro.Context parameter to all the DSL methods, we no longer need to be careful to access unstable request reference (in the DynamicScope)
  • Add Async* traits to disable the global thread-local variable (e.g.) AsyncSkinnyMicroServlet, AsyncSkinnyMicroFilter
class Sample extends AsyncSkinnyMicroServlet {
  before() { implicit ctx =>
    // never directly access dynamic scope here
    contentType = "application/json; charset=utf-8"
  }

  get("/") { implicit ctx =>
    // never directly access dynamic scope here

    // you no longer need to specify AsyncResult by yourself
    Future {
      // never directly access dynamic scope here
    }
  }
}

If you need to mix non-async and async, define implicit value in the main thread side and fix error: ambiguous implicit values compilation errors:

class Sample extends SkinnyMicroServlet {
  get("/") {
    implicit val ctx = context

    // you no longer need to specify AsyncResult by yourself
    Future {
      // fix compilation errors like this:
      params(ctx).get("name")
    }
  }
}

More type-safe web controllers

Newly added traits accepts not Any result but ActionResult or Future[ActionResult] instead. You don't need to work with Any result any more.

object Sample extends TypedSkinnyMicroServlet {
  get("/") {
    "ok" // compilation error!
  }

  get("/") {
    Ok("ok") // ActionResult
  }
}

See also:

Handy standalone web application builder

skinny-micro-server has its own embedded Jetty server. This library will be very handy when you need to quickly build a small web application.

https://github.com/skinny-framework/skinny-micro-heroku-example

The following is the minimum scalas example:

#!/usr/bin/env scalas
/***
scalaVersion := "2.11.7"
resolvers += "sonatype releases" at "https://oss.sonatype.org/content/repositories/releases"
libraryDependencies += "org.skinny-framework" %% "skinny-micro-server" % "0.9.12"
*/
import skinny.micro._
import scala.concurrent._

WebServer.mount(new WebApp {
  get("/") {
    val name = params.getOrElse("name", "Anonymous")
    s"Hello, $name"
  }
}).mount(new AsyncWebApp {
  get("/async") { implicit ctx =>
    Future {
      responseAsJSON(params)
    }
  }
}).port(8081).run()

NOTICE: WebApp is a type alias for SkinnyMicroFilter and AsyncWebApp is one for AsyncSkinnyMicroFilter.

ScalikeJDBC 2.3.0

Skinny ORM 2.0.0 is compatible with ScalikeJDBC 2.3.x. The biggest change of ScalikeJDBC 2.3 is switching its default connection pool impelmentation from commons-dbcp 1.4 to commons-dbcp2 (2.1.1).

json4s 3.3.0

Skinny Micro's json4s integration is compatible with not json4s 3.2.x but only 3.3.x or higher. Be careful when you need to work with json4s 3.2.x compatible libs in your Skinny 2 apps.