001: /**
002: * Copyright (C) 2006 Google Inc.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */package com.google.inject;
016:
017: import com.google.inject.InjectorImpl.SingleMemberInjector;
018: import com.google.inject.Key.AnnotationStrategy;
019: import static com.google.inject.Scopes.SINGLETON;
020: import com.google.inject.matcher.Matcher;
021: import com.google.inject.spi.Message;
022: import com.google.inject.spi.SourceProviders;
023: import com.google.inject.util.Annotations;
024: import static com.google.inject.util.Objects.nonNull;
025: import com.google.inject.util.StackTraceElements;
026: import com.google.inject.util.Stopwatch;
027: import java.lang.annotation.Annotation;
028: import java.lang.reflect.Member;
029: import java.lang.reflect.Method;
030: import java.util.ArrayList;
031: import java.util.Collection;
032: import java.util.Collections;
033: import java.util.HashMap;
034: import java.util.HashSet;
035: import java.util.List;
036: import java.util.Map;
037: import java.util.Set;
038: import java.util.logging.Level;
039: import java.util.logging.Logger;
040: import org.aopalliance.intercept.MethodInterceptor;
041:
042: /**
043: * Builds a dependency injection {@link Injector}. Binds {@link Key}s to
044: * implementations.
045: *
046: *
047: * @author crazybob@google.com (Bob Lee)
048: */
049: class BinderImpl implements Binder {
050:
051: static {
052: SourceProviders.skip(BinderImpl.class);
053: }
054:
055: private static final Logger logger = Logger
056: .getLogger(BinderImpl.class.getName());
057:
058: final List<BindingBuilderImpl<?>> bindingBuilders = new ArrayList<BindingBuilderImpl<?>>();
059: final List<ConstantBindingBuilderImpl> constantBindingBuilders = new ArrayList<ConstantBindingBuilderImpl>();
060: final Map<Class<? extends Annotation>, Scope> scopes = new HashMap<Class<? extends Annotation>, Scope>();
061:
062: final List<StaticInjection> staticInjections = new ArrayList<StaticInjection>();
063:
064: InjectorImpl injector;
065:
066: final Stage stage;
067:
068: final Collection<Message> errorMessages = new ArrayList<Message>();
069:
070: private static final InternalFactory<Injector> INJECTOR_FACTORY = new InternalFactory<Injector>() {
071: public Injector get(InternalContext context) {
072: return context.getInjectorImpl();
073: }
074:
075: public String toString() {
076: return "Provider<Injector>";
077: }
078: };
079:
080: private static final InternalFactory<Logger> LOGGER_FACTORY = new InternalFactory<Logger>() {
081: // not test-covered?
082: public Logger get(InternalContext context) {
083: Member member = context.getExternalContext().getMember();
084: return member == null ? Logger.getAnonymousLogger()
085: : Logger.getLogger(member.getDeclaringClass()
086: .getName());
087: }
088:
089: public String toString() {
090: return "Provider<Logger>";
091: }
092: };
093:
094: final ProxyFactoryBuilder proxyFactoryBuilder;
095:
096: /**
097: * Constructs a new builder.
098: *
099: * @param stage we're running in. If the stage is {@link Stage#PRODUCTION},
100: * we will eagerly load singletons.
101: */
102: public BinderImpl(Stage stage) {
103: bindScope(Singleton.class, SINGLETON);
104:
105: bind(Injector.class).to(INJECTOR_FACTORY);
106: bind(Logger.class).to(LOGGER_FACTORY);
107: bind(Stage.class).toInstance(stage);
108:
109: this .proxyFactoryBuilder = new ProxyFactoryBuilder();
110:
111: this .stage = stage;
112: }
113:
114: /**
115: * Constructs a new builder for a development environment (see
116: * {@link Stage#DEVELOPMENT}).
117: */
118: public BinderImpl() {
119: this (Stage.DEVELOPMENT);
120: }
121:
122: public Stage currentStage() {
123: return stage;
124: }
125:
126: final List<CreationListener> creationListeners = new ArrayList<CreationListener>();
127: final List<CreationListener> instanceInjectors = new ArrayList<CreationListener>();
128:
129: interface CreationListener {
130: void notify(InjectorImpl injector);
131: }
132:
133: public void bindInterceptor(Matcher<? super Class<?>> classMatcher,
134: Matcher<? super Method> methodMatcher,
135: MethodInterceptor... interceptors) {
136: proxyFactoryBuilder.intercept(classMatcher, methodMatcher,
137: interceptors);
138: }
139:
140: public void bindScope(Class<? extends Annotation> annotationType,
141: Scope scope) {
142: if (!Scopes.isScopeAnnotation(annotationType)) {
143: addError(StackTraceElements.forType(annotationType),
144: ErrorMessages.MISSING_SCOPE_ANNOTATION);
145: // Go ahead and bind anyway so we don't get collateral errors.
146: }
147:
148: if (!Annotations.isRetainedAtRuntime(annotationType)) {
149: addError(StackTraceElements.forType(annotationType),
150: ErrorMessages.MISSING_RUNTIME_RETENTION, source());
151: // Go ahead and bind anyway so we don't get collateral errors.
152: }
153:
154: Scope existing = scopes.get(nonNull(annotationType,
155: "annotation type"));
156: if (existing != null) {
157: addError(source(), ErrorMessages.DUPLICATE_SCOPES,
158: existing, annotationType, scope);
159: } else {
160: scopes.put(annotationType, nonNull(scope, "scope"));
161: }
162: }
163:
164: public <T> BindingBuilderImpl<T> bind(Key<T> key) {
165: BindingBuilderImpl<T> builder = new BindingBuilderImpl<T>(this ,
166: key, source());
167: bindingBuilders.add(builder);
168: return builder;
169: }
170:
171: public <T> BindingBuilderImpl<T> bind(TypeLiteral<T> typeLiteral) {
172: return bind(Key.get(typeLiteral));
173: }
174:
175: public <T> BindingBuilderImpl<T> bind(Class<T> clazz) {
176: return bind(Key.get(clazz));
177: }
178:
179: public ConstantBindingBuilderImpl bindConstant() {
180: ConstantBindingBuilderImpl constantBuilder = new ConstantBindingBuilderImpl(
181: this , source());
182: constantBindingBuilders.add(constantBuilder);
183: return constantBuilder;
184: }
185:
186: public void requestStaticInjection(Class<?>... types) {
187: staticInjections.add(new StaticInjection(source(), types));
188: }
189:
190: public void install(Module module) {
191: module.configure(this );
192: }
193:
194: public void addError(String message, Object... arguments) {
195: configurationErrorHandler.handle(source(), message, arguments);
196: }
197:
198: public void addError(Throwable t) {
199: Object source = source();
200: String className = t.getClass().getSimpleName();
201: String message = ErrorMessages.getRootMessage(t);
202: String logMessage = String.format(
203: ErrorMessages.EXCEPTION_REPORTED_BY_MODULE, message);
204: logger.log(Level.INFO, logMessage, t);
205: addError(source,
206: ErrorMessages.EXCEPTION_REPORTED_BY_MODULE_SEE_LOG,
207: message);
208: }
209:
210: void addError(Object source, String message, Object... arguments) {
211: configurationErrorHandler.handle(source, message, arguments);
212: }
213:
214: void addError(Object source, String message) {
215: configurationErrorHandler.handle(source, message);
216: }
217:
218: /**
219: * Adds an error message to be reported at creation time.
220: */
221: void add(Message errorMessage) {
222: errorMessages.add(errorMessage);
223: }
224:
225: final Stopwatch stopwatch = new Stopwatch();
226:
227: /**
228: * Creates a {@link Injector} instance. Injects static members for classes
229: * which were registered using {@link #requestStaticInjection(Class...)}.
230: *
231: * @throws CreationException if configuration errors are found. The
232: * expectation is that the application will log this exception and exit.
233: * @throws IllegalStateException if called more than once
234: */
235: Injector createInjector() throws CreationException {
236: stopwatch.resetAndLog(logger, "Configuration");
237:
238: Map<Key<?>, BindingImpl<?>> bindings = new HashMap<Key<?>, BindingImpl<?>>();
239: injector = new InjectorImpl(proxyFactoryBuilder.create(),
240: bindings, scopes);
241: injector.setErrorHandler(configurationErrorHandler);
242:
243: createConstantBindings();
244:
245: // Commands to execute before returning the Injector instance.
246: final List<ContextualCallable<Void>> preloaders = new ArrayList<ContextualCallable<Void>>();
247:
248: createBindings(preloaders);
249:
250: stopwatch.resetAndLog(logger, "Binding creation");
251:
252: injector.index();
253:
254: stopwatch.resetAndLog(logger, "Binding indexing");
255:
256: for (CreationListener creationListener : creationListeners) {
257: creationListener.notify(injector);
258: }
259:
260: stopwatch.resetAndLog(logger, "Validation");
261:
262: for (StaticInjection staticInjection : staticInjections) {
263: staticInjection.createMemberInjectors(injector);
264: }
265:
266: stopwatch.resetAndLog(logger, "Static validation");
267:
268: // Blow up if we encountered errors.
269: if (!errorMessages.isEmpty()) {
270: throw new CreationException(errorMessages);
271: }
272:
273: // Switch to runtime error handling.
274: injector.setErrorHandler(new RuntimeErrorHandler());
275:
276: // Inject static members.
277: for (StaticInjection staticInjection : staticInjections) {
278: staticInjection.runMemberInjectors(injector);
279: }
280:
281: stopwatch.resetAndLog(logger, "Static member injection");
282:
283: // Inject pre-existing instances.
284: for (CreationListener instanceInjector : instanceInjectors) {
285: instanceInjector.notify(injector);
286: }
287:
288: stopwatch.resetAndLog(logger, "Instance injection");
289:
290: // Run preloading commands.
291: runPreloaders(injector, preloaders);
292:
293: stopwatch.resetAndLog(logger, "Preloading");
294:
295: return injector;
296: }
297:
298: private void runPreloaders(InjectorImpl injector,
299: final List<ContextualCallable<Void>> preloaders) {
300: injector.callInContext(new ContextualCallable<Void>() {
301: public Void call(InternalContext context) {
302: for (ContextualCallable<Void> preloader : preloaders) {
303: preloader.call(context);
304: }
305: return null;
306: }
307: });
308: }
309:
310: private void createBindings(
311: List<ContextualCallable<Void>> preloaders) {
312: for (BindingBuilderImpl<?> builder : bindingBuilders) {
313: createBinding(builder, preloaders);
314: }
315: }
316:
317: private <T> void createBinding(BindingBuilderImpl<T> builder,
318: List<ContextualCallable<Void>> preloaders) {
319: final Key<T> key = builder.getKey();
320: final InternalFactory<? extends T> factory = builder
321: .getInternalFactory(injector);
322: BindingImpl<?> binding = BindingImpl.newInstance(injector, key,
323: builder.getSource(), factory);
324:
325: putBinding(binding);
326:
327: // Register to preload if necessary.
328: boolean preload = stage == Stage.PRODUCTION;
329: if (builder.isSingletonScoped()) {
330: if (preload || builder.shouldPreload()) {
331: preloaders.add(new BindingPreloader(key, factory));
332: }
333: } else {
334: if (builder.shouldPreload()) {
335: addError(builder.getSource(),
336: ErrorMessages.PRELOAD_NOT_ALLOWED);
337: }
338: }
339: }
340:
341: private void createConstantBindings() {
342: for (ConstantBindingBuilderImpl builder : constantBindingBuilders) {
343: createConstantBinding(builder);
344: }
345: }
346:
347: private void createConstantBinding(
348: ConstantBindingBuilderImpl builder) {
349: if (builder.hasValue()) {
350: putBinding(builder.createBinding(injector));
351: } else {
352: addError(builder.getSource(),
353: ErrorMessages.MISSING_CONSTANT_VALUE);
354: }
355: }
356:
357: private static Set<Class<?>> FORBIDDEN_TYPES = forbiddenTypes();
358:
359: private static Set<Class<?>> forbiddenTypes() {
360: Set<Class<?>> set = new HashSet<Class<?>>();
361: Collections.addAll(
362: set,
363:
364: // It's unfortunate that we have to maintain a blacklist of specific
365: // classes, but we can't easily block the whole package because of
366: // all our unit tests.
367:
368: AbstractModule.class, Binder.class, Binding.class,
369: Key.class, Module.class, Provider.class, Scope.class,
370: TypeLiteral.class);
371: return Collections.unmodifiableSet(set);
372: }
373:
374: void putBinding(BindingImpl<?> binding) {
375: Key<?> key = binding.getKey();
376: Map<Key<?>, BindingImpl<?>> bindings = injector
377: .internalBindings();
378: Binding<?> original = bindings.get(key);
379:
380: Class<?> rawType = key.getRawType();
381: if (FORBIDDEN_TYPES.contains(rawType)) {
382: addError(binding.getSource(),
383: ErrorMessages.CANNOT_BIND_TO_GUICE_TYPE, rawType
384: .getSimpleName());
385: return;
386: }
387:
388: if (bindings.containsKey(key)) {
389: addError(binding.getSource(),
390: ErrorMessages.BINDING_ALREADY_SET, key, original
391: .getSource());
392: } else {
393: bindings.put(key, binding);
394: }
395: }
396:
397: /**
398: * Gets the current source.
399: */
400: Object source() {
401: return SourceProviders.defaultSource();
402: }
403:
404: ErrorHandler configurationErrorHandler = new AbstractErrorHandler() {
405:
406: public void handle(Object source, String message) {
407: add(new Message(source, message));
408: }
409: };
410:
411: /**
412: * Handles errors after the injector is created.
413: */
414: static class RuntimeErrorHandler extends AbstractErrorHandler {
415:
416: static ErrorHandler INSTANCE = new RuntimeErrorHandler();
417:
418: public void handle(Object source, String message) {
419: throw new ConfigurationException("Error at " + source + " "
420: + message);
421: }
422: }
423:
424: /**
425: * A requested static injection.
426: */
427: class StaticInjection {
428:
429: final Object source;
430: final Class<?>[] types;
431: final List<SingleMemberInjector> memberInjectors = new ArrayList<SingleMemberInjector>();
432:
433: public StaticInjection(Object source, Class<?>[] types) {
434: this .source = source;
435: this .types = types;
436: }
437:
438: void createMemberInjectors(final InjectorImpl injector) {
439: injector.withDefaultSource(source, new Runnable() {
440: public void run() {
441: for (Class<?> clazz : types) {
442: injector.addSingleInjectorsForFields(clazz
443: .getDeclaredFields(), true,
444: memberInjectors);
445: injector.addSingleInjectorsForMethods(clazz
446: .getDeclaredMethods(), true,
447: memberInjectors);
448: }
449: }
450: });
451: }
452:
453: void runMemberInjectors(InjectorImpl injector) {
454: injector.callInContext(new ContextualCallable<Void>() {
455: public Void call(InternalContext context) {
456: for (SingleMemberInjector injector : memberInjectors) {
457: injector.inject(context, null);
458: }
459: return null;
460: }
461: });
462: }
463: }
464:
465: static class BindingPreloader implements ContextualCallable<Void> {
466:
467: private final Key<?> key;
468: private final InternalFactory<?> factory;
469:
470: public BindingPreloader(Key<?> key, InternalFactory<?> factory) {
471: this .key = key;
472: this .factory = factory;
473: }
474:
475: public Void call(InternalContext context) {
476: ExternalContext<?> externalContext = ExternalContext
477: .newInstance(null, key, context.getInjectorImpl());
478: context.setExternalContext(externalContext);
479: try {
480: factory.get(context);
481: return null;
482: } finally {
483: context.setExternalContext(null);
484: }
485: }
486: }
487: }
|