KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > google > inject > BinderImpl


1 /**
2  * Copyright (C) 2006 Google Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16
17 package com.google.inject;
18
19 import com.google.inject.InjectorImpl.SingleMemberInjector;
20 import com.google.inject.Key.AnnotationStrategy;
21 import static com.google.inject.Scopes.SINGLETON;
22 import com.google.inject.matcher.Matcher;
23 import com.google.inject.spi.Message;
24 import com.google.inject.spi.SourceProviders;
25 import com.google.inject.util.Annotations;
26 import static com.google.inject.util.Objects.nonNull;
27 import com.google.inject.util.StackTraceElements;
28 import com.google.inject.util.Stopwatch;
29 import java.lang.annotation.Annotation JavaDoc;
30 import java.lang.reflect.Member JavaDoc;
31 import java.lang.reflect.Method JavaDoc;
32 import java.util.ArrayList JavaDoc;
33 import java.util.Collection JavaDoc;
34 import java.util.Collections JavaDoc;
35 import java.util.HashMap JavaDoc;
36 import java.util.HashSet JavaDoc;
37 import java.util.List JavaDoc;
38 import java.util.Map JavaDoc;
39 import java.util.Set JavaDoc;
40 import java.util.logging.Level JavaDoc;
41 import java.util.logging.Logger JavaDoc;
42 import org.aopalliance.intercept.MethodInterceptor;
43
44 /**
45  * Builds a dependency injection {@link Injector}. Binds {@link Key}s to
46  * implementations.
47  *
48  *
49  * @author crazybob@google.com (Bob Lee)
50  */

51 class BinderImpl implements Binder {
52
53   static {
54     SourceProviders.skip(BinderImpl.class);
55   }
56
57   private static final Logger JavaDoc logger
58       = Logger.getLogger(BinderImpl.class.getName());
59
60   final List JavaDoc<BindingBuilderImpl<?>> bindingBuilders
61       = new ArrayList JavaDoc<BindingBuilderImpl<?>>();
62   final List JavaDoc<ConstantBindingBuilderImpl> constantBindingBuilders
63       = new ArrayList JavaDoc<ConstantBindingBuilderImpl>();
64   final Map JavaDoc<Class JavaDoc<? extends Annotation JavaDoc>, Scope> scopes =
65       new HashMap JavaDoc<Class JavaDoc<? extends Annotation JavaDoc>, Scope>();
66
67   final List JavaDoc<StaticInjection> staticInjections
68       = new ArrayList JavaDoc<StaticInjection>();
69
70   InjectorImpl injector;
71
72   final Stage stage;
73
74   final Collection JavaDoc<Message> errorMessages = new ArrayList JavaDoc<Message>();
75
76   private static final InternalFactory<Injector> INJECTOR_FACTORY
77       = new InternalFactory<Injector>() {
78     public Injector get(InternalContext context) {
79       return context.getInjectorImpl();
80     }
81
82     public String JavaDoc toString() {
83       return "Provider<Injector>";
84     }
85   };
86
87   private static final InternalFactory<Logger JavaDoc> LOGGER_FACTORY
88       = new InternalFactory<Logger JavaDoc>() {
89     // not test-covered?
90
public Logger JavaDoc get(InternalContext context) {
91       Member JavaDoc member = context.getExternalContext().getMember();
92       return member == null
93           ? Logger.getAnonymousLogger()
94           : Logger.getLogger(member.getDeclaringClass().getName());
95     }
96
97     public String JavaDoc toString() {
98       return "Provider<Logger>";
99     }
100   };
101
102   final ProxyFactoryBuilder proxyFactoryBuilder;
103
104   /**
105    * Constructs a new builder.
106    *
107    * @param stage we're running in. If the stage is {@link Stage#PRODUCTION},
108    * we will eagerly load singletons.
109    */

110   public BinderImpl(Stage stage) {
111     bindScope(Singleton.class, SINGLETON);
112
113     bind(Injector.class).to(INJECTOR_FACTORY);
114     bind(Logger JavaDoc.class).to(LOGGER_FACTORY);
115     bind(Stage.class).toInstance(stage);
116
117     this.proxyFactoryBuilder = new ProxyFactoryBuilder();
118
119     this.stage = stage;
120   }
121
122   /**
123    * Constructs a new builder for a development environment (see
124    * {@link Stage#DEVELOPMENT}).
125    */

126   public BinderImpl() {
127     this(Stage.DEVELOPMENT);
128   }
129
130   public Stage currentStage() {
131     return stage;
132   }
133
134   final List JavaDoc<CreationListener> creationListeners
135       = new ArrayList JavaDoc<CreationListener>();
136   final List JavaDoc<CreationListener> instanceInjectors
137       = new ArrayList JavaDoc<CreationListener>();
138
139   interface CreationListener {
140     void notify(InjectorImpl injector);
141   }
142
143   public void bindInterceptor(Matcher<? super Class JavaDoc<?>> classMatcher,
144       Matcher<? super Method JavaDoc> methodMatcher, MethodInterceptor... interceptors) {
145     proxyFactoryBuilder.intercept(classMatcher, methodMatcher, interceptors);
146   }
147
148   public void bindScope(Class JavaDoc<? extends Annotation JavaDoc> annotationType,
149       Scope scope) {
150     if (!Scopes.isScopeAnnotation(annotationType)) {
151       addError(StackTraceElements.forType(annotationType),
152           ErrorMessages.MISSING_SCOPE_ANNOTATION);
153       // Go ahead and bind anyway so we don't get collateral errors.
154
}
155
156     if (!Annotations.isRetainedAtRuntime(annotationType)) {
157       addError(StackTraceElements.forType(annotationType),
158           ErrorMessages.MISSING_RUNTIME_RETENTION, source());
159       // Go ahead and bind anyway so we don't get collateral errors.
160
}
161
162     Scope existing = scopes.get(nonNull(annotationType, "annotation type"));
163     if (existing != null) {
164       addError(source(), ErrorMessages.DUPLICATE_SCOPES, existing,
165           annotationType, scope);
166     }
167     else {
168       scopes.put(annotationType, nonNull(scope, "scope"));
169     }
170   }
171
172   public <T> BindingBuilderImpl<T> bind(Key<T> key) {
173     BindingBuilderImpl<T> builder =
174         new BindingBuilderImpl<T>(this, key, source());
175     bindingBuilders.add(builder);
176     return builder;
177   }
178
179   public <T> BindingBuilderImpl<T> bind(TypeLiteral<T> typeLiteral) {
180     return bind(Key.get(typeLiteral));
181   }
182
183   public <T> BindingBuilderImpl<T> bind(Class JavaDoc<T> clazz) {
184     return bind(Key.get(clazz));
185   }
186
187   public ConstantBindingBuilderImpl bindConstant() {
188     ConstantBindingBuilderImpl constantBuilder
189         = new ConstantBindingBuilderImpl(this, source());
190     constantBindingBuilders.add(constantBuilder);
191     return constantBuilder;
192   }
193
194   public void requestStaticInjection(Class JavaDoc<?>... types) {
195     staticInjections.add(new StaticInjection(source(), types));
196   }
197
198   public void install(Module module) {
199     module.configure(this);
200   }
201
202   public void addError(String JavaDoc message, Object JavaDoc... arguments) {
203     configurationErrorHandler.handle(source(), message, arguments);
204   }
205
206   public void addError(Throwable JavaDoc t) {
207     Object JavaDoc source = source();
208     String JavaDoc className = t.getClass().getSimpleName();
209     String JavaDoc message = ErrorMessages.getRootMessage(t);
210     String JavaDoc logMessage = String.format(
211         ErrorMessages.EXCEPTION_REPORTED_BY_MODULE, message);
212     logger.log(Level.INFO, logMessage, t);
213     addError(source, ErrorMessages.EXCEPTION_REPORTED_BY_MODULE_SEE_LOG, message);
214   }
215
216   void addError(Object JavaDoc source, String JavaDoc message, Object JavaDoc... arguments) {
217     configurationErrorHandler.handle(source, message, arguments);
218   }
219
220   void addError(Object JavaDoc source, String JavaDoc message) {
221     configurationErrorHandler.handle(source, message);
222   }
223
224   /**
225    * Adds an error message to be reported at creation time.
226    */

227   void add(Message errorMessage) {
228     errorMessages.add(errorMessage);
229   }
230
231   final Stopwatch stopwatch = new Stopwatch();
232
233   /**
234    * Creates a {@link Injector} instance. Injects static members for classes
235    * which were registered using {@link #requestStaticInjection(Class...)}.
236    *
237    * @throws CreationException if configuration errors are found. The
238    * expectation is that the application will log this exception and exit.
239    * @throws IllegalStateException if called more than once
240    */

241   Injector createInjector() throws CreationException {
242     stopwatch.resetAndLog(logger, "Configuration");
243
244     Map JavaDoc<Key<?>, BindingImpl<?>> bindings = new HashMap JavaDoc<Key<?>, BindingImpl<?>>();
245     injector = new InjectorImpl(
246         proxyFactoryBuilder.create(), bindings, scopes);
247     injector.setErrorHandler(configurationErrorHandler);
248
249     createConstantBindings();
250
251     // Commands to execute before returning the Injector instance.
252
final List JavaDoc<ContextualCallable<Void JavaDoc>> preloaders
253         = new ArrayList JavaDoc<ContextualCallable<Void JavaDoc>>();
254
255     createBindings(preloaders);
256
257     stopwatch.resetAndLog(logger, "Binding creation");
258
259     injector.index();
260
261     stopwatch.resetAndLog(logger, "Binding indexing");
262
263     for (CreationListener creationListener : creationListeners) {
264       creationListener.notify(injector);
265     }
266
267     stopwatch.resetAndLog(logger, "Validation");
268
269     for (StaticInjection staticInjection : staticInjections) {
270       staticInjection.createMemberInjectors(injector);
271     }
272
273     stopwatch.resetAndLog(logger, "Static validation");
274
275     // Blow up if we encountered errors.
276
if (!errorMessages.isEmpty()) {
277       throw new CreationException(errorMessages);
278     }
279
280     // Switch to runtime error handling.
281
injector.setErrorHandler(new RuntimeErrorHandler());
282
283     // Inject static members.
284
for (StaticInjection staticInjection : staticInjections) {
285       staticInjection.runMemberInjectors(injector);
286     }
287
288     stopwatch.resetAndLog(logger, "Static member injection");
289
290     // Inject pre-existing instances.
291
for (CreationListener instanceInjector : instanceInjectors) {
292       instanceInjector.notify(injector);
293     }
294
295     stopwatch.resetAndLog(logger, "Instance injection");
296
297     // Run preloading commands.
298
runPreloaders(injector, preloaders);
299
300     stopwatch.resetAndLog(logger, "Preloading");
301
302     return injector;
303   }
304
305   private void runPreloaders(InjectorImpl injector,
306       final List JavaDoc<ContextualCallable<Void JavaDoc>> preloaders) {
307     injector.callInContext(new ContextualCallable<Void JavaDoc>() {
308       public Void JavaDoc call(InternalContext context) {
309         for (ContextualCallable<Void JavaDoc> preloader : preloaders) {
310           preloader.call(context);
311         }
312         return null;
313       }
314     });
315   }
316
317   private void createBindings(List JavaDoc<ContextualCallable<Void JavaDoc>> preloaders) {
318     for (BindingBuilderImpl<?> builder : bindingBuilders) {
319       createBinding(builder, preloaders);
320     }
321   }
322
323   private <T> void createBinding(BindingBuilderImpl<T> builder,
324       List JavaDoc<ContextualCallable<Void JavaDoc>> preloaders) {
325     final Key<T> key = builder.getKey();
326     final InternalFactory<? extends T> factory
327         = builder.getInternalFactory(injector);
328     BindingImpl<?> binding
329         = BindingImpl.newInstance(injector, key, builder.getSource(), factory);
330
331     putBinding(binding);
332
333     // Register to preload if necessary.
334
boolean preload = stage == Stage.PRODUCTION;
335     if (builder.isSingletonScoped()) {
336       if (preload || builder.shouldPreload()) {
337         preloaders.add(new BindingPreloader(key, factory));
338       }
339     }
340     else {
341       if (builder.shouldPreload()) {
342         addError(builder.getSource(), ErrorMessages.PRELOAD_NOT_ALLOWED);
343       }
344     }
345   }
346
347   private void createConstantBindings() {
348     for (ConstantBindingBuilderImpl builder : constantBindingBuilders) {
349       createConstantBinding(builder);
350     }
351   }
352
353   private void createConstantBinding(ConstantBindingBuilderImpl builder) {
354     if (builder.hasValue()) {
355       putBinding(builder.createBinding(injector));
356     }
357     else {
358       addError(builder.getSource(), ErrorMessages.MISSING_CONSTANT_VALUE);
359     }
360   }
361
362   private static Set JavaDoc<Class JavaDoc<?>> FORBIDDEN_TYPES = forbiddenTypes();
363
364   private static Set JavaDoc<Class JavaDoc<?>> forbiddenTypes() {
365     Set JavaDoc<Class JavaDoc<?>> set = new HashSet JavaDoc<Class JavaDoc<?>>();
366     Collections.addAll(set,
367
368         // It's unfortunate that we have to maintain a blacklist of specific
369
// classes, but we can't easily block the whole package because of
370
// all our unit tests.
371

372         AbstractModule.class,
373         Binder.class,
374         Binding.class,
375         Key.class,
376         Module.class,
377         Provider.class,
378         Scope.class,
379         TypeLiteral.class);
380     return Collections.unmodifiableSet(set);
381   }
382
383   void putBinding(BindingImpl<?> binding) {
384     Key<?> key = binding.getKey();
385     Map JavaDoc<Key<?>, BindingImpl<?>> bindings = injector.internalBindings();
386     Binding<?> original = bindings.get(key);
387
388     Class JavaDoc<?> rawType = key.getRawType();
389     if (FORBIDDEN_TYPES.contains(rawType)) {
390       addError(binding.getSource(), ErrorMessages.CANNOT_BIND_TO_GUICE_TYPE,
391           rawType.getSimpleName());
392       return;
393     }
394
395     if (bindings.containsKey(key)) {
396       addError(binding.getSource(), ErrorMessages.BINDING_ALREADY_SET, key,
397           original.getSource());
398     }
399     else {
400       bindings.put(key, binding);
401     }
402   }
403
404   /**
405    * Gets the current source.
406    */

407   Object JavaDoc source() {
408     return SourceProviders.defaultSource();
409   }
410
411   ErrorHandler configurationErrorHandler = new AbstractErrorHandler() {
412
413     public void handle(Object JavaDoc source, String JavaDoc message) {
414       add(new Message(source, message));
415     }
416   };
417
418   /**
419    * Handles errors after the injector is created.
420    */

421   static class RuntimeErrorHandler extends AbstractErrorHandler {
422
423     static ErrorHandler INSTANCE = new RuntimeErrorHandler();
424
425     public void handle(Object JavaDoc source, String JavaDoc message) {
426       throw new ConfigurationException("Error at " + source + " " + message);
427     }
428   }
429
430   /**
431    * A requested static injection.
432    */

433   class StaticInjection {
434
435     final Object JavaDoc source;
436     final Class JavaDoc<?>[] types;
437     final List JavaDoc<SingleMemberInjector> memberInjectors
438         = new ArrayList JavaDoc<SingleMemberInjector>();
439
440     public StaticInjection(Object JavaDoc source, Class JavaDoc<?>[] types) {
441       this.source = source;
442       this.types = types;
443     }
444
445     void createMemberInjectors(final InjectorImpl injector) {
446       injector.withDefaultSource(source,
447           new Runnable JavaDoc() {
448             public void run() {
449               for (Class JavaDoc<?> clazz : types) {
450                 injector.addSingleInjectorsForFields(
451                     clazz.getDeclaredFields(), true, memberInjectors);
452                 injector.addSingleInjectorsForMethods(
453                     clazz.getDeclaredMethods(), true, memberInjectors);
454               }
455             }
456           });
457     }
458
459     void runMemberInjectors(InjectorImpl injector) {
460       injector.callInContext(new ContextualCallable<Void JavaDoc>() {
461         public Void JavaDoc call(InternalContext context) {
462           for (SingleMemberInjector injector : memberInjectors) {
463             injector.inject(context, null);
464           }
465           return null;
466         }
467       });
468     }
469   }
470
471   static class BindingPreloader implements ContextualCallable<Void JavaDoc> {
472
473     private final Key<?> key;
474     private final InternalFactory<?> factory;
475
476     public BindingPreloader(Key<?> key, InternalFactory<?> factory) {
477       this.key = key;
478       this.factory = factory;
479     }
480
481     public Void JavaDoc call(InternalContext context) {
482       ExternalContext<?> externalContext
483           = ExternalContext.newInstance(null, key, context.getInjectorImpl());
484       context.setExternalContext(externalContext);
485       try {
486         factory.get(context);
487         return null;
488       }
489       finally {
490         context.setExternalContext(null);
491       }
492     }
493   }
494 }
495
Popular Tags