/* * Copyright (C) 2009-2015 Typesafe Inc. <http://www.typesafe.com> */ package play.api.inject import java.lang.reflect.Constructor import play.{ Configuration => JavaConfiguration, Environment => JavaEnvironment } import play.api._ import play.utils.PlayIO import scala.annotation.varargs import scala.reflect.ClassTag /** * A Play dependency injection module. * * Dependency injection modules can be used by Play plugins to provide bindings for JSR-330 compliant * ApplicationLoaders. Any plugin that wants to provide components that a Play application can use may implement * one of these. * * Providing custom modules can be done by appending their fully qualified class names to `play.modules.enabled` in * `application.conf`, for example * * {{{ * play.modules.enabled += "com.example.FooModule" * play.modules.enabled += "com.example.BarModule" * }}} * * It is strongly advised that in addition to providing a module for JSR-330 DI, that plugins also provide a Scala * trait that constructs the modules manually. This allows for use of the module without needing a runtime dependency * injection provider. * * The `bind` methods are provided only as a DSL for specifying bindings. For example: * * {{{ * def bindings(env: Environment, conf: Configuration) = Seq( * bind[Foo].to[FooImpl], * bind[Bar].to(new Bar()), * bind[Foo].qualifiedWith[SomeQualifier].to[OtherFoo] * ) * }}} */ abstract class Module { /** * Get the bindings provided by this module. * * Implementations are strongly encouraged to do *nothing* in this method other than provide bindings. Startup * should be handled in the the constructors and/or providers bound in the returned bindings. Dependencies on other * modules or components should be expressed through constructor arguments. * * The configuration and environment a provided for the purpose of producing dynamic bindings, for example, if what * gets bound depends on some configuration, this may be read to control that. * * @param environment The environment * @param configuration The configuration * @return A sequence of bindings */ def bindings(environment: Environment, configuration: Configuration): Seq[Binding[_]] /** * Create a binding key for the given class. */ final def bind[T](clazz: Class[T]): BindingKey[T] = play.api.inject.bind(clazz) /** * Create a binding key for the given class. */ final def bind[T: ClassTag]: BindingKey[T] = play.api.inject.bind[T] /** * Create a seq. * * For Java compatibility. */ @varargs final def seq(bindings: Binding[_]*): Seq[Binding[_]] = bindings } /** * Locates and loads modules from the Play environment. */ object Modules { /** * Locate the modules from the environment. * * Loads all modules specified by the play.modules.enabled property, minus the modules specified by the * play.modules.disabled property. If the modules have constructors that take an `Environment` and a * `Configuration`, then these constructors are called first; otherwise default constructors are called. * * @param environment The environment. * @param configuration The configuration. * @return A sequence of objects. This method makes no attempt to cast or check the types of the modules being loaded, * allowing ApplicationLoader implementations to reuse the same mechanism to load modules specific to them. */ def locate(environment: Environment, configuration: Configuration): Seq[Any] = { val includes = configuration.getStringSeq("play.modules.enabled").getOrElse(Seq.empty) val excludes = configuration.getStringSeq("play.modules.disabled").getOrElse(Seq.empty) val moduleClassNames = includes.toSet -- excludes moduleClassNames.map { className => try { val clazz = environment.classLoader.loadClass(className) def tryConstruct(args: AnyRef*): Option[Any] = { val ctor: Option[Constructor[_]] = try { val argTypes = args.map(_.getClass) Some(clazz.getConstructor(argTypes: _*)) } catch { case _: NoSuchMethodException => None case _: SecurityException => None } ctor.map(_.newInstance(args: _*)) } { tryConstruct(environment, configuration) } orElse { tryConstruct(new JavaEnvironment(environment), new JavaConfiguration(configuration)) } orElse { tryConstruct() } getOrElse { throw new PlayException("No valid constructors", "Module [" + className + "] cannot be instantiated.") } } catch { case e: PlayException => throw e case e: VirtualMachineError => throw e case e: ThreadDeath => throw e case e: Throwable => throw new PlayException( "Cannot load module", "Module [" + className + "] cannot be instantiated.", e) } }.toSeq } }