/framework/src/play/src/main/scala/play/api/inject/guice/GuiceInjectorBuilder.scala
Scala | 343 lines | 185 code | 48 blank | 110 comment | 2 complexity | 647b0a19afe8714c2e60406979fe682e MD5 | raw file
1/*
2 * Copyright (C) 2009-2015 Typesafe Inc. <http://www.typesafe.com>
3 */
4package play.api.inject
5package guice
6
7import com.google.inject.util.{ Modules => GuiceModules, Providers => GuiceProviders }
8import com.google.inject.{ Module => GuiceModule, Stage, CreationException, Guice }
9import java.io.File
10import javax.inject.Inject
11import play.api.inject.{ Binding => PlayBinding, BindingKey, Injector => PlayInjector, Module => PlayModule }
12import play.api.{ Configuration, Environment, Mode, PlayException }
13import scala.reflect.ClassTag
14
15class GuiceLoadException(message: String) extends RuntimeException(message)
16
17/**
18 * A builder for creating Guice-backed Play Injectors.
19 */
20abstract class GuiceBuilder[Self] protected (
21 environment: Environment,
22 configuration: Configuration,
23 modules: Seq[GuiceableModule],
24 overrides: Seq[GuiceableModule],
25 disabled: Seq[Class[_]],
26 eagerly: Boolean) {
27
28 /**
29 * Set the environment.
30 */
31 final def in(env: Environment): Self =
32 copyBuilder(environment = env)
33
34 /**
35 * Set the environment path.
36 */
37 final def in(path: File): Self =
38 copyBuilder(environment = environment.copy(rootPath = path))
39
40 /**
41 * Set the environment mode.
42 */
43 final def in(mode: Mode.Mode): Self =
44 copyBuilder(environment = environment.copy(mode = mode))
45
46 /**
47 * Set the environment class loader.
48 */
49 final def in(classLoader: ClassLoader): Self =
50 copyBuilder(environment = environment.copy(classLoader = classLoader))
51
52 /**
53 * Set the dependency initialization to eager.
54 */
55 final def eagerlyLoaded(): Self =
56 copyBuilder(eagerly = true)
57
58 /**
59 * Add additional configuration.
60 */
61 final def configure(conf: Configuration): Self =
62 copyBuilder(configuration = configuration ++ conf)
63
64 /**
65 * Add additional configuration.
66 */
67 final def configure(conf: Map[String, Any]): Self =
68 configure(Configuration.from(conf))
69
70 /**
71 * Add additional configuration.
72 */
73 final def configure(conf: (String, Any)*): Self =
74 configure(conf.toMap)
75
76 /**
77 * Add Guice modules, Play modules, or Play bindings.
78 *
79 * @see [[GuiceableModuleConversions]] for the automatically available implicit
80 * conversions to [[GuiceableModule]] from modules and bindings.
81 */
82 final def bindings(bindModules: GuiceableModule*): Self =
83 copyBuilder(modules = modules ++ bindModules)
84
85 /**
86 * Override bindings using Guice modules, Play modules, or Play bindings.
87 *
88 * @see [[GuiceableModuleConversions]] for the automatically available implicit
89 * conversions to [[GuiceableModule]] from modules and bindings.
90 */
91 final def overrides(overrideModules: GuiceableModule*): Self =
92 copyBuilder(overrides = overrides ++ overrideModules)
93
94 /**
95 * Disable modules by class.
96 */
97 final def disable(moduleClasses: Class[_]*): Self =
98 copyBuilder(disabled = disabled ++ moduleClasses)
99
100 /**
101 * Disable module by class.
102 */
103 final def disable[T](implicit tag: ClassTag[T]): Self = disable(tag.runtimeClass)
104
105 /**
106 * Create a Play Injector backed by Guice using this configured builder.
107 */
108 def applicationModule(): GuiceModule = createModule
109
110 /**
111 * Creation of the Guice Module used by the injector.
112 * Libraries like Guiceberry and Jukito that want to handle injector creation may find this helpful.
113 */
114 def createModule(): GuiceModule = {
115 import scala.collection.JavaConverters._
116 val injectorModule = GuiceableModule.guice(Seq(
117 bind[PlayInjector].to[GuiceInjector],
118 // Java API injector is bound here so that it's available in both
119 // the default application loader and the Java Guice builders
120 bind[play.inject.Injector].to[play.inject.DelegateInjector]
121 ))
122 val enabledModules = modules.map(_.disable(disabled))
123 val bindingModules = GuiceableModule.guiced(environment, configuration)(enabledModules) :+ injectorModule
124 val overrideModules = GuiceableModule.guiced(environment, configuration)(overrides)
125 GuiceModules.`override`(bindingModules.asJava).`with`(overrideModules.asJava)
126 }
127
128 /**
129 * Create a Play Injector backed by Guice using this configured builder.
130 */
131 def injector(): PlayInjector = {
132 try {
133 val stage = environment.mode match {
134 case Mode.Prod => Stage.PRODUCTION
135 case _ if eagerly => Stage.PRODUCTION
136 case _ => Stage.DEVELOPMENT
137 }
138 val guiceInjector = Guice.createInjector(stage, applicationModule())
139 guiceInjector.getInstance(classOf[PlayInjector])
140 } catch {
141 case e: CreationException => e.getCause match {
142 case p: PlayException => throw p
143 case _ => throw e
144 }
145 }
146 }
147
148 /**
149 * Internal copy method with defaults.
150 */
151 private def copyBuilder(
152 environment: Environment = environment,
153 configuration: Configuration = configuration,
154 modules: Seq[GuiceableModule] = modules,
155 overrides: Seq[GuiceableModule] = overrides,
156 disabled: Seq[Class[_]] = disabled,
157 eagerly: Boolean = eagerly): Self =
158 newBuilder(environment, configuration, modules, overrides, disabled, eagerly)
159
160 /**
161 * Create a new Self for this immutable builder.
162 * Provided by builder implementations.
163 */
164 protected def newBuilder(
165 environment: Environment,
166 configuration: Configuration,
167 modules: Seq[GuiceableModule],
168 overrides: Seq[GuiceableModule],
169 disabled: Seq[Class[_]],
170 eagerly: Boolean): Self
171
172}
173
174/**
175 * Default empty builder for creating Guice-backed Injectors.
176 */
177final class GuiceInjectorBuilder(
178 environment: Environment = Environment.simple(),
179 configuration: Configuration = Configuration.empty,
180 modules: Seq[GuiceableModule] = Seq.empty,
181 overrides: Seq[GuiceableModule] = Seq.empty,
182 disabled: Seq[Class[_]] = Seq.empty,
183 eagerly: Boolean = false) extends GuiceBuilder[GuiceInjectorBuilder](
184 environment, configuration, modules, overrides, disabled, eagerly
185) {
186
187 // extra constructor for creating from Java
188 def this() = this(environment = Environment.simple())
189
190 /**
191 * Create a Play Injector backed by Guice using this configured builder.
192 */
193 def build(): PlayInjector = injector()
194
195 protected def newBuilder(
196 environment: Environment,
197 configuration: Configuration,
198 modules: Seq[GuiceableModule],
199 overrides: Seq[GuiceableModule],
200 disabled: Seq[Class[_]],
201 eagerly: Boolean): GuiceInjectorBuilder =
202 new GuiceInjectorBuilder(environment, configuration, modules, overrides, disabled, eagerly)
203}
204
205/**
206 * Magnet pattern for creating Guice modules from Play modules or bindings.
207 */
208trait GuiceableModule {
209 def guiced(env: Environment, conf: Configuration): Seq[GuiceModule]
210 def disable(classes: Seq[Class[_]]): GuiceableModule
211}
212
213/**
214 * Loading and converting Guice modules.
215 */
216object GuiceableModule extends GuiceableModuleConversions {
217
218 def loadModules(environment: Environment, configuration: Configuration): Seq[GuiceableModule] = {
219 Modules.locate(environment, configuration) map guiceable
220 }
221
222 /**
223 * Attempt to convert a module of unknown type to a GuiceableModule.
224 */
225 def guiceable(module: Any): GuiceableModule = module match {
226 case playModule: PlayModule => fromPlayModule(playModule)
227 case guiceModule: GuiceModule => fromGuiceModule(guiceModule)
228 case unknown => throw new PlayException(
229 "Unknown module type",
230 s"Module [$unknown] is not a Play module or a Guice module"
231 )
232 }
233
234 /**
235 * Apply GuiceableModules to create Guice modules.
236 */
237 def guiced(env: Environment, conf: Configuration)(builders: Seq[GuiceableModule]): Seq[GuiceModule] =
238 builders flatMap { module => module.guiced(env, conf) }
239
240}
241
242/**
243 * Implicit conversions to GuiceableModules.
244 */
245trait GuiceableModuleConversions {
246
247 import scala.language.implicitConversions
248
249 implicit def fromGuiceModule(guiceModule: GuiceModule): GuiceableModule = fromGuiceModules(Seq(guiceModule))
250
251 implicit def fromGuiceModules(guiceModules: Seq[GuiceModule]): GuiceableModule = new GuiceableModule {
252 def guiced(env: Environment, conf: Configuration): Seq[GuiceModule] = guiceModules
253 def disable(classes: Seq[Class[_]]): GuiceableModule = fromGuiceModules(filterOut(classes, guiceModules))
254 override def toString = s"GuiceableModule(${guiceModules.mkString(", ")})"
255 }
256
257 implicit def fromPlayModule(playModule: PlayModule): GuiceableModule = fromPlayModules(Seq(playModule))
258
259 implicit def fromPlayModules(playModules: Seq[PlayModule]): GuiceableModule = new GuiceableModule {
260 def guiced(env: Environment, conf: Configuration): Seq[GuiceModule] = playModules.map(guice(env, conf))
261 def disable(classes: Seq[Class[_]]): GuiceableModule = fromPlayModules(filterOut(classes, playModules))
262 override def toString = s"GuiceableModule(${playModules.mkString(", ")})"
263 }
264
265 implicit def fromPlayBinding(binding: PlayBinding[_]): GuiceableModule = fromPlayBindings(Seq(binding))
266
267 implicit def fromPlayBindings(bindings: Seq[PlayBinding[_]]): GuiceableModule = new GuiceableModule {
268 def guiced(env: Environment, conf: Configuration): Seq[GuiceModule] = Seq(guice(bindings))
269 def disable(classes: Seq[Class[_]]): GuiceableModule = this // no filtering
270 override def toString = s"GuiceableModule(${bindings.mkString(", ")})"
271 }
272
273 private def filterOut[A](classes: Seq[Class[_]], instances: Seq[A]): Seq[A] =
274 instances.filterNot(o => classes.exists(_.isAssignableFrom(o.getClass)))
275
276 /**
277 * Convert the given Play module to a Guice module.
278 */
279 def guice(env: Environment, conf: Configuration)(module: PlayModule): GuiceModule =
280 guice(module.bindings(env, conf))
281
282 /**
283 * Convert the given Play bindings to a Guice module.
284 */
285 def guice(bindings: Seq[PlayBinding[_]]): GuiceModule = {
286 new com.google.inject.AbstractModule {
287 def configure(): Unit = {
288 for (b <- bindings) {
289 val binding = b.asInstanceOf[PlayBinding[Any]]
290 val builder = binder().withSource(binding).bind(GuiceKey(binding.key))
291 binding.target.foreach {
292 case ProviderTarget(provider) => builder.toProvider(GuiceProviders.guicify(provider))
293 case ProviderConstructionTarget(provider) => builder.toProvider(provider)
294 case ConstructionTarget(implementation) => builder.to(implementation)
295 case BindingKeyTarget(key) => builder.to(GuiceKey(key))
296 }
297 (binding.scope, binding.eager) match {
298 case (Some(scope), false) => builder.in(scope)
299 case (None, true) => builder.asEagerSingleton()
300 case (Some(scope), true) => throw new GuiceLoadException("A binding must either declare a scope or be eager: " + binding)
301 case _ => // do nothing
302 }
303 }
304 }
305 }
306 }
307
308}
309
310/**
311 * Conversion from Play BindingKey to Guice Key.
312 */
313object GuiceKey {
314 import com.google.inject.Key
315
316 def apply[T](key: BindingKey[T]): Key[T] = {
317 key.qualifier match {
318 case Some(QualifierInstance(instance)) => Key.get(key.clazz, instance)
319 case Some(QualifierClass(clazz)) => Key.get(key.clazz, clazz)
320 case None => Key.get(key.clazz)
321 }
322 }
323}
324
325/**
326 * Play Injector backed by a Guice Injector.
327 */
328class GuiceInjector @Inject() (injector: com.google.inject.Injector) extends PlayInjector {
329 /**
330 * Get an instance of the given class from the injector.
331 */
332 def instanceOf[T](implicit ct: ClassTag[T]) = instanceOf(ct.runtimeClass.asInstanceOf[Class[T]])
333
334 /**
335 * Get an instance of the given class from the injector.
336 */
337 def instanceOf[T](clazz: Class[T]) = injector.getInstance(clazz)
338
339 /**
340 * Get an instance bound to the given binding key.
341 */
342 def instanceOf[T](key: BindingKey[T]) = injector.getInstance(GuiceKey(key))
343}