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