All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.coursera.naptime.NaptimeModule.scala Maven / Gradle / Ivy

There is a newer version: 0.9.0-alpha5
Show newest version
/*
 * Copyright 2016 Coursera Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.coursera.naptime

import com.google.inject.AbstractModule
import com.google.inject.Binder
import com.linkedin.data.schema.DataSchema
import net.codingwell.scalaguice.ScalaMapBinder
import net.codingwell.scalaguice.ScalaModule
import net.codingwell.scalaguice.ScalaMultibinder
import org.coursera.naptime.resources.CollectionResource
import org.coursera.naptime.router2.ResourceRouterBuilder
import org.coursera.naptime.router2.Router

import scala.collection.immutable
import scala.reflect.macros.blackbox
import scala.language.experimental.macros
import scala.reflect.ClassTag

/*
 * Work around implementation limitation. See:
 * http://stackoverflow.com/questions/17557057/how-to-solve-implementation-restriction-trait-accesses-protected-method
 */
private[naptime] abstract class BinderExposer extends AbstractModule {
  protected[this] def exposedBinder() = binder()
}

/**
 * Inherit from this trait to bind resources and schema types.
 *
 * Naptime builds upon Guice to discover and find all resources in the service, as well
 * as to discover the reverse mappings from high-level class types to their wire
 * representations.
 */
trait NaptimeModule extends BinderExposer with ScalaModule {

  private[this] def resourceBinder = NaptimeModuleHelpers.multibinder(exposedBinder())
  private[this] def schemaTypesBinder = NaptimeModuleHelpers.schemaTypesBinder(exposedBinder())

  protected[this] def bindResourceRouterBuilder(
      resourceRouterBuilder: ResourceRouterBuilder): Unit = {
    resourceBinder.addBinding.toInstance(resourceRouterBuilder)
  }

  /**
   * Register a resource with Guice, and builds the router.
   *
   * Example:
   * {{{
   *   override def configure(): Unit = {
   *     bindResource[MyResource]
   *   }
   * }}}
   *
   * @tparam ResourceClass The resource to bind.
   */
  protected[this] def bindResource[ResourceClass <: CollectionResource[_, _, _]]: Unit =
    macro NaptimeModuleHelpers.bindResourceImpl[ResourceClass]

  /**
   * Set a binding from a high-level data type to the wire representation.
   *
   * Example:
   * {{{
   * override def configure(): Unit = {
   *   bindSchemaType[java.util.Date](
   *     DataSchemaUtil.dataSchemaTypeToPrimitiveDataSchema(DataSchema.Type.LONG))
   * }
   * }}}
   * @tparam Type The type to register.
   */
  protected[this] def bindSchemaType[Type](schema: DataSchema)(implicit ct: ClassTag[Type]): Unit = {
    schemaTypesBinder.addBinding(ct.runtimeClass.getName).toInstance(schema)
  }
}

private[naptime] object NaptimeModuleHelpers {

  /**
   * Constructs a ScalaMultibinder used for binding resources given a binder.
   *
   * Note: the binder is actually for the ResourceRouterBuilders and not for either the
   * ResourceRouters, nor for the Resources themselves.
   */
  private[naptime] def multibinder(binder: Binder) =
    ScalaMultibinder.newSetBinder[ResourceRouterBuilder](binder)

  private[naptime] def schemaTypesBinder(binder: Binder) =
    ScalaMapBinder.newMapBinder[String, DataSchema](binder)


  /**
   * Macro implementation for the [[NaptimeModule.bindResource]] function.
   */
  def bindResourceImpl[Resource <: CollectionResource[_, _, _]](c: blackbox.Context)
    (implicit wtt: c.WeakTypeTag[Resource]): c.Tree = {
    import c.universe._
    val routerObject = typeOf[Router.type]
    q"bindResourceRouterBuilder($routerObject.build[$wtt])"
  }

}

/**
 * Install this singleton module into your service to configure Naptime defaults to ensure a
 * successful boot.
 *
 * {{{
 *   install(NaptimeModule)
 * }}}
 *
 * Note: as of writing this comment, there is no need to install this module in your service for
 * correct operation.
 */
object NaptimeModule extends ScalaModule {
  type SchemaTypeOverrides = immutable.Map[String, DataSchema]

  /**
   * Sets default bindings.
   */
  override def configure(): Unit = {
    // Register the multi-bindings in order to prevent guice errors if no resource is bound.
    NaptimeModuleHelpers.multibinder(binder())
    NaptimeModuleHelpers.schemaTypesBinder(binder())
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy