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 com.bm.ejb3guice.internal.StackTraceElements;
018:
019: import java.lang.annotation.Annotation;
020: import java.lang.reflect.Constructor;
021: import java.lang.reflect.InvocationTargetException;
022:
023: /**
024: * Injects constructors.
025: *
026: * @author crazybob@google.com (Bob Lee)
027: */
028: class ConstructorInjector<T> {
029:
030: final Class<T> implementation;
031:
032: final InjectorImpl.SingleMemberInjector[] memberInjectors;
033:
034: final InjectorImpl.SingleParameterInjector<?>[] parameterInjectors;
035:
036: final ConstructionProxy<T> constructionProxy;
037:
038: final CreationListner creationListner;
039:
040: final Class<? extends Annotation>[] injectionMarkers;
041:
042: ConstructorInjector(InjectorImpl injector, Class<T> implementation) {
043: this .implementation = implementation;
044: injectionMarkers = (injector.injectionMarkers == null) ? Ejb3Guice
045: .markerToArray(Inject.class)
046: : injector.injectionMarkers;
047: creationListner = injector.creationListener;
048: Constructor<T> constructor = findConstructorIn(injector,
049: implementation);
050: parameterInjectors = createParameterInjector(injector,
051: constructor);
052: memberInjectors = injector.injectors.get(implementation)
053: .toArray(new InjectorImpl.SingleMemberInjector[0]);
054: constructionProxy = injector.constructionProxyFactory
055: .get(constructor);
056: }
057:
058: /**
059: * Used to create an invalid injector.
060: */
061: private ConstructorInjector() {
062: implementation = null;
063: memberInjectors = null;
064: parameterInjectors = null;
065: constructionProxy = null;
066: creationListner = null;
067: injectionMarkers = Ejb3Guice.markerToArray(Inject.class);
068: }
069:
070: InjectorImpl.SingleParameterInjector<?>[] createParameterInjector(
071: InjectorImpl injector, Constructor<T> constructor) {
072: try {
073: return constructor.getParameterTypes().length == 0 ? null // default
074: // constructor.
075: : injector.getParametersInjectors(constructor,
076: constructor.getParameterAnnotations(),
077: constructor.getGenericParameterTypes());
078: } catch (InjectorImpl.MissingDependencyException e) {
079: e.handle(injector.errorHandler);
080: return null;
081: }
082: }
083:
084: private Constructor<T> findConstructorIn(InjectorImpl injector,
085: Class<T> implementation) {
086: Constructor<T> found = null;
087: @SuppressWarnings("unchecked")
088: Constructor<T>[] constructors = (Constructor<T>[]) implementation
089: .getDeclaredConstructors();
090: for (Constructor<T> constructor : constructors) {
091: for (Class<? extends Annotation> currentMarker : injectionMarkers) {
092: Annotation inject = constructor
093: .getAnnotation(currentMarker);
094: if (inject != null) {
095: if (inject instanceof Inject
096: && ((Inject) inject).optional()) {
097: injector.errorHandler.handle(StackTraceElements
098: .forMember(constructor),
099: ErrorMessages.OPTIONAL_CONSTRUCTOR);
100: }
101:
102: if (found != null) {
103: injector.errorHandler.handle(StackTraceElements
104: .forMember(found),
105: ErrorMessages.TOO_MANY_CONSTRUCTORS);
106: return InjectorImpl.invalidConstructor();
107: }
108: found = constructor;
109: }
110: }
111: }
112: if (found != null) {
113: return found;
114: }
115:
116: // If no annotated constructor is found, look for a no-arg constructor
117: // instead.
118: try {
119: return implementation.getDeclaredConstructor();
120: } catch (NoSuchMethodException e) {
121: injector.errorHandler.handle(
122: StackTraceElements.forMember(implementation
123: .getDeclaredConstructors()[0]),
124: ErrorMessages.MISSING_CONSTRUCTOR, implementation);
125: return InjectorImpl.invalidConstructor();
126: }
127: }
128:
129: /**
130: * Construct an instance. Returns {@code Object} instead of {@code T}
131: * because it may return a proxy.
132: */
133: Object construct(InternalContext context, Class<?> expectedType) {
134: ConstructionContext<T> constructionContext = context
135: .getConstructionContext(this );
136:
137: // We have a circular reference between constructors. Return a proxy.
138: if (constructionContext.isConstructing()) {
139: // TODO (crazybob): if we can't proxy this object, can we proxy the
140: // other object?
141: return constructionContext.createProxy(expectedType);
142: }
143:
144: // If we're re-entering this factory while injecting fields or methods,
145: // return the same instance. This prevents infinite loops.
146: T t = constructionContext.getCurrentReference();
147: if (t != null) {
148: return t;
149: }
150:
151: try {
152: // First time through...
153: constructionContext.startConstruction();
154: try {
155: Object[] parameters = InjectorImpl.getParameters(
156: context, parameterInjectors);
157: t = constructionProxy.newInstance(parameters);
158: if (this .creationListner != null) {
159: this .creationListner.afterCreation(t);
160: }
161: constructionContext.setProxyDelegates(t);
162: } finally {
163: constructionContext.finishConstruction();
164: }
165:
166: // Store reference. If an injector re-enters this factory, they'll
167: // get the same reference.
168: constructionContext.setCurrentReference(t);
169:
170: // Inject fields and methods.
171: for (InjectorImpl.SingleMemberInjector injector : memberInjectors) {
172: injector.inject(context, t);
173: }
174:
175: return t;
176: } catch (InvocationTargetException e) {
177: throw new RuntimeException(e);
178: } finally {
179: constructionContext.removeCurrentReference();
180: }
181: }
182:
183: /**
184: * Returns an invalid constructor. This enables us to keep running and
185: * reporting legitimate errors.
186: */
187: static <T> ConstructorInjector<T> invalidConstructor() {
188: return new ConstructorInjector<T>() {
189: Object construct(InternalContext context,
190: Class<?> expectedType) {
191: throw new UnsupportedOperationException();
192: }
193:
194: public T get() {
195: throw new UnsupportedOperationException();
196: }
197: };
198: }
199: }
|