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