LiftRESTful

Implementing a Lift RestHelper activator

Posted on by

At ersocon I often build RESTful APIs using Lift. For several reasons I tend to create own services for each resource. Usually you have to add those services to Lifts dispatcher by creating a method like "init" and add the service.

object CategoryService extends RestHelper {

  /**
   * Add service to lift dispatch process
   */
  def init(): Unit = {
    
    LiftRules.statelessDispatch.append(this)
    
  }

}

In your Boot class you can then easily call to register the service:

CategoryService.init()

But what if you have a bunch of services in a bigger project? Sometimes I forget to add a service to my Boot class. Furthermore my boot class gets "polluted" by just a list of services. To simplify things I created a trait called "RestHelperActivator" which contains only the init() method. By adding a RestHelperActivator to our CategoryService and removing the init method we can use reflection to register all services in the Boot class by utilizing the RestHelperActivator trait:

trait RestHelperActivator {
  self: RestHelper =>

  /**
   * Add service to lift dispatch process
   */
  def init(): Unit = {
    
    LiftRules.statelessDispatch.append(this)
    
  }
}

Extending the service with the activator:

object CategoryService extends RestHelper with RestHelperActivator {

}

Scanning for services in out RestHelperActivator trait:


/**
 * Scans the services package and appends services
 */
protected def appendServices(): Unit = {
  val reflections = new Reflections(
    new ConfigurationBuilder().setScanners(new SubTypesScanner(false), new ResourcesScanner())
      .setUrls(ClasspathHelper.forPackage("here.goes.the.package.with.services"))
  )

  val subTypes = reflections.getSubTypesOf(classOf[RestHelperActivator]).asScala.toList

  subTypes.foreach(clazz => {
    if (clazz.getFields.toList.filter(_.contains("MODULE$")).size > 0) {
      val restAppender: RestHelperActivator = clazz.getField("MODULE$")
        .get(classOf[RestHelperActivator]).asInstanceOf[RestHelperActivator]

      restAppender.init()
    }
  })
}

By scanning the package, collectiong all RestHelperActivator subtypes and matching against objects we can call the init method on each service which is placed in the given package.