1
2
3
4
5 package mockit.internal.injection.full;
6
7 import static java.lang.reflect.Modifier.isStatic;
8
9 import static mockit.internal.injection.InjectionPoint.CONVERSATION_CLASS;
10 import static mockit.internal.injection.InjectionPoint.INJECT_CLASS;
11 import static mockit.internal.injection.InjectionPoint.PERSISTENCE_UNIT_CLASS;
12 import static mockit.internal.injection.InjectionPoint.SERVLET_CLASS;
13 import static mockit.internal.reflection.ConstructorReflection.newInstanceUsingDefaultConstructorIfAvailable;
14 import static mockit.internal.util.Utilities.getClassType;
15
16 import edu.umd.cs.findbugs.annotations.NonNull;
17 import edu.umd.cs.findbugs.annotations.Nullable;
18
19 import java.lang.annotation.Annotation;
20 import java.lang.reflect.ParameterizedType;
21 import java.lang.reflect.Type;
22 import java.lang.reflect.TypeVariable;
23 import java.util.logging.Logger;
24
25 import javax.annotation.Resource;
26 import javax.enterprise.context.Conversation;
27 import javax.inject.Provider;
28 import javax.inject.Singleton;
29 import javax.sql.CommonDataSource;
30
31 import mockit.asm.jvmConstants.Access;
32 import mockit.internal.injection.InjectionPoint;
33 import mockit.internal.injection.InjectionProvider;
34 import mockit.internal.injection.InjectionState;
35 import mockit.internal.injection.Injector;
36 import mockit.internal.injection.TestedClass;
37 import mockit.internal.injection.TestedObjectCreation;
38
39
40
41
42 public final class FullInjection {
43 private static final int INVALID_TYPES = Access.ABSTRACT + Access.ANNOTATION + Access.ENUM;
44
45 @NonNull
46 private final InjectionState injectionState;
47 @NonNull
48 private final String testedClassName;
49 @NonNull
50 private final String testedName;
51 @Nullable
52 private final ServletDependencies servletDependencies;
53 @Nullable
54 private final JPADependencies jpaDependencies;
55 @Nullable
56 private Class<?> dependencyClass;
57 @Nullable
58 private InjectionProvider parentInjectionProvider;
59
60 public FullInjection(@NonNull InjectionState injectionState, @NonNull Class<?> testedClass,
61 @NonNull String testedName) {
62 this.injectionState = injectionState;
63 testedClassName = testedClass.getSimpleName();
64 this.testedName = testedName;
65 servletDependencies = SERVLET_CLASS == null ? null : new ServletDependencies(injectionState);
66 jpaDependencies = PERSISTENCE_UNIT_CLASS == null ? null : new JPADependencies(injectionState);
67 }
68
69 @Nullable
70 public Object createOrReuseInstance(@NonNull TestedClass testedClass, @NonNull Injector injector,
71 @Nullable InjectionProvider injectionProvider, @Nullable String qualifiedName) {
72 setInjectionProvider(injectionProvider);
73
74 InjectionPoint injectionPoint = getInjectionPoint(testedClass, injectionProvider, qualifiedName);
75 Object dependency = injectionState.getInstantiatedDependency(testedClass, injectionPoint);
76
77 if (dependency != null) {
78 return dependency;
79 }
80
81 Class<?> typeToInject = dependencyClass;
82
83 if (typeToInject == Logger.class) {
84 return createLogger(testedClass);
85 }
86
87 if (typeToInject == null || !isInstantiableType(typeToInject)) {
88 return null;
89 }
90
91 return createInstance(testedClass, injector, injectionProvider, injectionPoint);
92 }
93
94 public void setInjectionProvider(@Nullable InjectionProvider injectionProvider) {
95 if (injectionProvider != null) {
96 injectionProvider.parent = parentInjectionProvider;
97 }
98
99 parentInjectionProvider = injectionProvider;
100 }
101
102 @NonNull
103 private InjectionPoint getInjectionPoint(@NonNull TestedClass testedClass,
104 @Nullable InjectionProvider injectionProvider, @Nullable String qualifiedName) {
105 if (injectionProvider == null) {
106 dependencyClass = testedClass.targetClass;
107 return new InjectionPoint(dependencyClass, qualifiedName, true);
108 }
109
110 Type dependencyType = injectionProvider.getDeclaredType();
111
112 if (dependencyType instanceof TypeVariable<?>) {
113 dependencyType = testedClass.reflection.resolveTypeVariable((TypeVariable<?>) dependencyType);
114 dependencyClass = getClassType(dependencyType);
115 } else {
116 dependencyClass = injectionProvider.getClassOfDeclaredType();
117 }
118
119 if (qualifiedName != null && !qualifiedName.isEmpty()) {
120 return new InjectionPoint(dependencyClass, qualifiedName, true);
121 }
122
123 if (jpaDependencies != null && JPADependencies.isApplicable(dependencyClass)) {
124 for (Annotation annotation : injectionProvider.getAnnotations()) {
125 InjectionPoint injectionPoint = jpaDependencies.getInjectionPointIfAvailable(annotation);
126
127 if (injectionPoint != null) {
128 return injectionPoint;
129 }
130 }
131 }
132
133 return new InjectionPoint(dependencyType, injectionProvider.getName(), false);
134 }
135
136 @NonNull
137 private static Object createLogger(@NonNull TestedClass testedClass) {
138 TestedClass testedClassWithLogger = testedClass.parent;
139 assert testedClassWithLogger != null;
140 return Logger.getLogger(testedClassWithLogger.nameOfTestedClass);
141 }
142
143 public static boolean isInstantiableType(@NonNull Class<?> type) {
144 if (type.isPrimitive() || type.isArray() || type.isAnnotation()) {
145 return false;
146 }
147
148 if (type.isInterface()) {
149 return true;
150 }
151
152 int typeModifiers = type.getModifiers();
153
154 if ((typeModifiers & INVALID_TYPES) != 0 || !isStatic(typeModifiers) && type.isMemberClass()) {
155 return false;
156 }
157
158 return type.getClassLoader() != null;
159 }
160
161 @Nullable
162 private Object createInstance(@NonNull TestedClass testedClass, @NonNull Injector injector,
163 @Nullable InjectionProvider injectionProvider, @NonNull InjectionPoint injectionPoint) {
164 @SuppressWarnings("ConstantConditions")
165 @NonNull
166 Class<?> typeToInject = dependencyClass;
167 Object dependency = null;
168
169 if (typeToInject.isInterface()) {
170 dependency = createInstanceOfSupportedInterfaceIfApplicable(testedClass, typeToInject, injectionPoint,
171 injectionProvider);
172
173 if (dependency == null && typeToInject.getClassLoader() != null) {
174 Class<?> resolvedType = injectionState.resolveInterface(typeToInject);
175
176 if (resolvedType != null && !resolvedType.isInterface()) {
177
178 testedClass = new TestedClass(resolvedType, resolvedType);
179 typeToInject = resolvedType;
180 }
181 }
182 }
183
184 if (dependency == null) {
185 dependency = createAndRegisterNewInstance(typeToInject, testedClass, injector, injectionPoint,
186 injectionProvider);
187 }
188
189 return dependency;
190 }
191
192 @Nullable
193 private Object createInstanceOfSupportedInterfaceIfApplicable(@NonNull TestedClass testedClass,
194 @NonNull Class<?> typeToInject, @NonNull InjectionPoint injectionPoint,
195 @Nullable InjectionProvider injectionProvider) {
196 Object dependency = null;
197
198 if (CommonDataSource.class.isAssignableFrom(typeToInject)) {
199 dependency = createAndRegisterDataSource(testedClass, injectionPoint, injectionProvider);
200 } else if (INJECT_CLASS != null && typeToInject == Provider.class) {
201 assert injectionProvider != null;
202 dependency = createProviderInstance(injectionProvider);
203 } else if (CONVERSATION_CLASS != null && typeToInject == Conversation.class) {
204 dependency = createAndRegisterConversationInstance();
205 } else if (servletDependencies != null && ServletDependencies.isApplicable(typeToInject)) {
206 dependency = servletDependencies.createAndRegisterDependency(typeToInject);
207 } else if (jpaDependencies != null && JPADependencies.isApplicable(typeToInject)) {
208 dependency = jpaDependencies.createAndRegisterDependency(typeToInject, injectionPoint, injectionProvider);
209 }
210
211 return dependency;
212 }
213
214 @Nullable
215 private Object createAndRegisterDataSource(@NonNull TestedClass testedClass, @NonNull InjectionPoint injectionPoint,
216 @Nullable InjectionProvider injectionProvider) {
217 if (injectionProvider == null || !injectionProvider.hasAnnotation(Resource.class)) {
218 return null;
219 }
220
221 TestDataSource dsCreation = new TestDataSource(injectionPoint);
222 CommonDataSource dataSource = dsCreation.createIfDataSourceDefinitionAvailable(testedClass);
223
224 if (dataSource != null) {
225 injectionState.saveInstantiatedDependency(injectionPoint, dataSource);
226 }
227
228 return dataSource;
229 }
230
231 @NonNull
232 private Object createProviderInstance(@NonNull InjectionProvider injectionProvider) {
233 ParameterizedType genericType = (ParameterizedType) injectionProvider.getDeclaredType();
234 final Class<?> providedClass = (Class<?>) genericType.getActualTypeArguments()[0];
235
236 if (providedClass.isAnnotationPresent(Singleton.class)) {
237 return new Provider<Object>() {
238 private Object dependency;
239
240 @Override
241 public synchronized Object get() {
242 if (dependency == null) {
243 dependency = createNewInstance(providedClass, true);
244 }
245
246 return dependency;
247 }
248 };
249 }
250
251 return (Provider<Object>) () -> createNewInstance(providedClass, false);
252 }
253
254 @Nullable
255 private Object createNewInstance(@NonNull Class<?> classToInstantiate, boolean required) {
256 if (classToInstantiate.isInterface()) {
257 return null;
258 }
259
260 if (classToInstantiate.getClassLoader() == null) {
261 return newInstanceUsingDefaultConstructorIfAvailable(classToInstantiate);
262 }
263
264 return new TestedObjectCreation(injectionState, this, classToInstantiate).create(required, false);
265 }
266
267 @NonNull
268 private Object createAndRegisterConversationInstance() {
269 Conversation conversation = new TestConversation();
270
271 InjectionPoint injectionPoint = new InjectionPoint(Conversation.class);
272 injectionState.saveInstantiatedDependency(injectionPoint, conversation);
273 return conversation;
274 }
275
276 @Nullable
277 private Object createAndRegisterNewInstance(@NonNull Class<?> typeToInstantiate, @NonNull TestedClass testedClass,
278 @NonNull Injector injector, @NonNull InjectionPoint injectionPoint,
279 @Nullable InjectionProvider injectionProvider) {
280 Object dependency = createNewInstance(typeToInstantiate,
281 injectionProvider != null && injectionProvider.isRequired());
282
283 if (dependency != null) {
284 if (injectionPoint.name == null) {
285 assert injectionProvider != null;
286 injectionPoint = new InjectionPoint(injectionPoint.type, injectionProvider.getName());
287 }
288
289 registerNewInstance(testedClass, injector, injectionPoint, dependency);
290 }
291
292 return dependency;
293 }
294
295 private void registerNewInstance(@NonNull TestedClass testedClass, @NonNull Injector injector,
296 @NonNull InjectionPoint injectionPoint, @NonNull Object dependency) {
297 injectionState.saveInstantiatedDependency(injectionPoint, dependency);
298
299 Class<?> instantiatedClass = dependency.getClass();
300
301 if (testedClass.isClassFromSameModuleOrSystemAsTestedClass(instantiatedClass)) {
302 injector.fillOutDependenciesRecursively(dependency, testedClass);
303 injectionState.lifecycleMethods.findLifecycleMethods(instantiatedClass);
304 injectionState.lifecycleMethods.executeInitializationMethodsIfAny(instantiatedClass, dependency);
305 }
306 }
307
308 @Override
309 public String toString() {
310 String description = "@Tested object \"" + testedClassName + ' ' + testedName + '"';
311
312 if (parentInjectionProvider != null) {
313 InjectionProvider injectionProvider = parentInjectionProvider.parent;
314
315 if (injectionProvider != null) {
316 description = injectionProvider + "\r\n of " + description;
317 }
318 }
319
320 return description;
321 }
322
323 public void clear() {
324 parentInjectionProvider = null;
325 }
326 }