MacWire 0.2: Scopes are simple!

MacWire generates new instance creation code of given classes, using values in the enclosing type for constructor parameters, with the help of Scala Macros. DI container replacement.

Version 0.2 has just landed!

First things first …

First, some bad news. Due to limitations of the current macros implementation in Scala (for more details see this discussion) in order to avoid occasional compilation errors it is necessary to add type ascriptions to the dependencies. This is a way of helping the type-checker that is invoked by the macro to figure out the types of the values which can be wired.

In practice it is good to always write the type ascription. For example:

trait MyModule {
   lazy val theUserFinder: UserFinder = wire[UserFinder]

This is a major inconvenience, but hopefully will get resolved some day. See the “Limitations” section in the README for more details.

Update 28/04/2013 As noted in the comments below, the recommendation for adding type ascriptions may be in fact relaxed by adding special-handling for declarations that are simple wire[X] expressions. So stay tuned for future releases for improvements :)

Also, it is worth noting that the type ascription may be a subtype of the wired type. This can be useful if you want to expose e.g. a trait that the wired class extends, instead of the full implementation.

… to the point

Now, to the good news. Especially when developing web applications, it is very useful to scope some of the dependencies, so that there’s a new instance for each request (but for each dependency usage, it is the same instance in one request), or that there’s a single instance per http session.

So far MacWire had two “built-in” scopes: singleton (declare the dependency as lazy val) and dependent (def). Now it is possible to create custom scopes, by implementing the Scope trait.

Scopes often seem “magical”; in traditional frameworks it is usually necessary to add an annotation to the class and then the dependency is automagically turned into a scoped one by the container.

In fact implementing a scoped bean is quite simple. We need two things. Firstly, instead of using instances directly, we need to use a proxy which delegates method calls to the “current instance”. What “current instance” means is scope-specific.

Secondly, we need a way to read a value from the scope, and store a value in the scope, if there’s no value yet. The two methods in the Scope trait correspond directly to the two cases outlined above.

Using a scope is quite straightforward; the scope of a dependency is declared in the wiring code, in the module. For example:

trait WebModule {
   lazy val loggedInUser: LoggedInUser = session(wire[LoggedInUser])
   def session: Scope

Note that the scope here is abstract (only the name suggests that it is a session scope). This has a couple of advantages. For testing, we can substitute a NoOpScope. Also, we can move the framework-integration code to the integration layer, or even provide a couple of implementations depending on the framework/container used.

MacWire comes with a Scope implementation targeted at synchronous web frameworks, ThreadLocalScope. The scope needs to be associated and disassociated with a scope storage. To implement a:

  • request scope, we need a new empty storage for every request (associateWithEmptyStorage method)
  • session scope, the storage (a Map) should be stored in the HttpSession (associate(Map) method)

This association can be done, for example, in a servlet filter:

class ScopeFilter(sessionScope: ThreadLocalScope) extends Filter {
  def init(filterConfig: FilterConfig) {}
  def doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain) {
    val httpRequest = request.asInstanceOf[HttpServletRequest]
    val sessionScopeStorage = new ScopeStorage {
      def get(key: String) = Option(httpRequest.getSession.getAttribute(key))
      def set(key: String, value: Any) {
        httpRequest.getSession.setAttribute(key, value)
    sessionScope.withStorage(sessionScopeStorage) {
      chain.doFilter(request, response)
  def destroy() {}

Note also that it is trivial to use a scoped value e.g. in an asynchronous process, where no web request is available: simply associate the scope with any storage, can be a temporary map. In traditional web frameworks this usually requires some amount of hacking.

To sum up, to use a scope in MacWire you need to:

  • declare a Scope in your module, use if for scoped dependencies
  • provide an implementation of the scope, e.g. ThreadLocalScope
  • associate the scope with a storage, e.g. in a servlet filter

You can see how this works in practice by browsing & running a Scalatra+MacWire example application. To run, clone MacWire and use this command: sbt examples-scalatra/run. To browse the code, simply head to GitHub.


The example contains a request-scoped dependency (SubmittedData), session-scoped dependency (LoggedInUser) and three main services (Service1, Service2, Service3). Each class has some dependencies (declared in the constructor), and is wired using wire[]. The code is organised in two modules: logic and servlet. The services have methods to return a string-status, containing the current request and session-scoped values, so that it is possible to verify that indeed the values are shared properly. The servlet module contains a servlet which shows the service status, plus the scopes implementations.

Note that the scopes subproject is completely independent, and can be used stand-alone with manual wiring or any other library/framework. Its only dependency is javassist.

Have fun!

  • Why is it necessary to supply a type ascription? Can’t the macro itself figure it out from the rhs of a lazy val / def?

  • Well it depends what the rhs is. If it is a simple wire[X], then sure, it would be easy to figure out. But in general it can be any expression. So maybe in fact it would be a good idea to add a special case for wire[X] and scopeInstace(wire[X]) expressions, and then require type ascriptions only for complex/other expressions.

    Even with arbitrary expressions, things work well when there’s a single module without scopes.

    With scopes, things get tricky, as the scope’s apply method takes an implicit type tag. And when I call context.typeCheck(rhsWithAScope), it fails, because it can’t find the implicit.

    With multiple modules, things get a bit weird (I don’t really understand the compilation ordering algorithm). Especially when the modules are in different packages, the inheriting module is compiled before the inherited. Then, to get the inherited module’s declarations, I need to type-check an expression of the type of the inherited module (as we discussed on the mailing list). And within that c.typeCheck invocation, I was getting some weird results from calling c.typeCheck on the rhs.

    Another case where a type ascription is needed for sure, is when there’s reference to the wired value within the trait (see the README – Limitations for an example); that causes recursive type errors.

    Overall, I guess I could add some special-common-cases handling, and relax the “always put a type ascription recommendation”. But to fully get rid of it, we would need post-typer macros, as discussed on the forums :)

  • 廖师虎

    Hello Adam, I use the Scalatra with MacWire 0.2, I copy your scalatra-example code, my example code, except the application boot class, I do not use jetty, This is my boot class(generate by g8):

    import com.lshoo.macwire.example._

    import com.lshoo.macwire.example.servlet.ServletModule

    import org.scalatra._

    import javax.servlet.ServletContext

    class ScalatraBootstrap extends LifeCycle {

    implicit val modules = new ServletModule {}

    override def init(context: ServletContext) {

    // load module

    context.addFilter(“MacWire Wiring”, modules.scopeFilter)

    context.mount(modules.servlet1, “/servlet/*”)

    context.mount(modules.authServlet, “/auth/*”)

    context.mount(new MacwireController, “/*”)



    when I use Chrome visit http://localhost:8080/servlet, report a error:

    This thread is not associated with a storage!

    java.lang.IllegalStateException: This thread is not associated with a storage!

    at com.softwaremill.macwire.scopes.ThreadLocalScopeStorage$class.throwNotAssociatedWithStorage(ThreadLocalScopeStorage.scala:81)

    at com.softwaremill.macwire.scopes.ThreadLocalScopeStorage$class.get(ThreadLocalScopeStorage.scala:12)

    at com.softwaremill.macwire.scopes.ThreadLocalScope.get(ThreadLocalScope.scala:3)

    at com.softwaremill.macwire.scopes.ProxyingScope$$anon$1.invoke(ProxyingScope.scala:20)

    at com.lshoo.macwire.example.logic.SubmittedData_$$_javassist_1.data_$eq(SubmittedData_$$

    at com.lshoo.macwire.example.servlet.Servlet1$$anonfun$1.apply(Servlet1.scala:16)

    at com.lshoo.macwire.example.servlet.Servlet1$$anonfun$1.apply(Servlet1.scala:15)

    at org.scalatra.ScalatraBase$$scalatra$ScalatraBase$$liftAction(ScalatraBase.scala:250)

    at org.scalatra.ScalatraBase$$anonfun$invoke$1.apply(ScalatraBase.scala:245)

    at org.scalatra.ScalatraBase$$anonfun$invoke$1.apply(ScalatraBase.scala:245)

    at org.scalatra.ScalatraBase$class.withRouteMultiParams(ScalatraBase.scala:321)

    at org.scalatra.ScalatraServlet.withRouteMultiParams(ScalatraServlet.scala:49)

    at org.scalatra.ScalatraBase$class.invoke(ScalatraBase.scala:244)

    at org.scalatra.ScalatraServlet.invoke(ScalatraServlet.scala:49)

    at org.scalatra.ScalatraBase$$anonfun$runRoutes$1$$anonfun$apply$7.apply(ScalatraBase.scala:220)

    at org.scalatra.ScalatraBase$$anonfun$runRoutes$1$$anonfun$apply$7.apply(ScalatraBase.scala:218)

    at scala.Option.flatMap(Option.scala:170)

    at org.scalatra.ScalatraBase$$anonfun$runRoutes$1.apply(ScalatraBase.scala:218)

    at org.scalatra.ScalatraBase$$anonfun$runRoutes$1.apply(ScalatraBase.scala:217)

    at scala.collection.immutable.Stream.flatMap(Stream.scala:442)

    at org.scalatra.ScalatraBase$class.runRoutes(ScalatraBase.scala:217)

    at org.scalatra.ScalatraServlet.runRoutes(ScalatraServlet.scala:49)

    at org.scalatra.ScalatraBase$class.runActions$1(ScalatraBase.scala:163)

    at org.scalatra.ScalatraBase$$anonfun$executeRoutes$1.apply$mcV$sp(ScalatraBase.scala:175)

    at org.scalatra.ScalatraBase$$anonfun$executeRoutes$1.apply(ScalatraBase.scala:175)

    at org.scalatra.ScalatraBase$$anonfun$executeRoutes$1.apply(ScalatraBase.scala:175)

    at org.scalatra.ScalatraBase$$scalatra$ScalatraBase$$cradleHalt(ScalatraBase.scala:190)

    at org.scalatra.ScalatraBase$class.executeRoutes(ScalatraBase.scala:175)

    at org.scalatra.ScalatraServlet.executeRoutes(ScalatraServlet.scala:49)

    at org.scalatra.ScalatraBase$$anonfun$handle$1.apply$mcV$sp(ScalatraBase.scala:113)

    at org.scalatra.ScalatraBase$$anonfun$handle$1.apply(ScalatraBase.scala:113)

    at org.scalatra.ScalatraBase$$anonfun$handle$1.apply(ScalatraBase.scala:113)

    at scala.util.DynamicVariable.withValue(DynamicVariable.scala:57)

    at org.scalatra.DynamicScope$class.withResponse(DynamicScope.scala:80)

    at org.scalatra.ScalatraServlet.withResponse(ScalatraServlet.scala:49)

    at org.scalatra.DynamicScope$$anonfun$withRequestResponse$1.apply(DynamicScope.scala:60)

    at scala.util.DynamicVariable.withValue(DynamicVariable.scala:57)

    at org.scalatra.DynamicScope$class.withRequest(DynamicScope.scala:71)

    at org.scalatra.ScalatraServlet.withRequest(ScalatraServlet.scala:49)

    at org.scalatra.DynamicScope$class.withRequestResponse(DynamicScope.scala:59)

    at org.scalatra.ScalatraServlet.withRequestResponse(ScalatraServlet.scala:49)

    at org.scalatra.ScalatraBase$class.handle(ScalatraBase.scala:111)


    at org.scalatra.servlet.ServletBase$class.handle(ServletBase.scala:43)


    at org.scalatra.scalate.ScalateSupport$class.handle(ScalateSupport.scala:122)

    at com.lshoo.macwire.example.servlet.Servlet1.handle(Servlet1.scala:14)

    at org.scalatra.ScalatraServlet.service(ScalatraServlet.scala:54)

    at javax.servlet.http.HttpServlet.service(

    at org.eclipse.jetty.servlet.ServletHolder.handle(

    at org.eclipse.jetty.servlet.ServletHandler.doHandle(

    at org.eclipse.jetty.server.handler.ScopedHandler.handle(


    at org.eclipse.jetty.server.session.SessionHandler.doHandle(

    at org.eclipse.jetty.server.handler.ContextHandler.__doHandle(

    at org.eclipse.jetty.server.handler.ContextHandler.doHandle(

    at org.eclipse.jetty.servlet.ServletHandler.doScope(

    at org.eclipse.jetty.server.session.SessionHandler.doScope(

    at org.eclipse.jetty.server.handler.ContextHandler.doScope(

    at org.eclipse.jetty.server.handler.ScopedHandler.handle(

    at org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(

    at org.eclipse.jetty.server.handler.HandlerWrapper.handle(

    at org.eclipse.jetty.server.Server.handle(

    at org.eclipse.jetty.server.AbstractHttpConnection.handleRequest(

    at org.eclipse.jetty.server.AbstractHttpConnection.headerComplete(

    at org.eclipse.jetty.server.AbstractHttpConnection$RequestHandler.headerComplete(

    at org.eclipse.jetty.http.HttpParser.parseNext(

    at org.eclipse.jetty.http.HttpParser.parseAvailable(

    at org.eclipse.jetty.server.AsyncHttpConnection.handle(



    at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(

    at org.eclipse.jetty.util.thread.QueuedThreadPool$


    What I do the example work?


  • I’m not entirely familiar with the new ServletContext API, but I think you need to an URL mapping for the filter. Currently you only added a filter, but it’s not mapped to any URLs.

    Looking at the Javadocs, the invocation should be something like this:

    context.mount(“…”, theFilter).addMappingForUrlPatterns(…)

  • 师虎 廖

    override def init(context: ServletContext) {

    // load module

    //context.addFilter(“MacWire Wiring”, modules.scopeFilter)

    context.addFilter(“MacWire”, modules.scopeFilter)

    .addMappingForUrlPatterns(java.util.EnumSet.allOf(classOf[DispatcherType]),true, “/*”)

    context.mount(modules.servlet1, “/servlet/*”)

    context.mount(modules.authServlet, “/auth/*”)

    context.mount(new MacwireController, “/*”)


    The boot class like this, It worked!