View Javadoc
1   /*
2    * MIT License
3    * Copyright (c) 2006-2025 JMockit developers
4    * See LICENSE file for full license text.
5    */
6   package mockit.internal.injection.full;
7   
8   import edu.umd.cs.findbugs.annotations.NonNull;
9   import edu.umd.cs.findbugs.annotations.Nullable;
10  
11  import java.beans.BeanInfo;
12  import java.beans.IntrospectionException;
13  import java.beans.Introspector;
14  import java.beans.PropertyDescriptor;
15  import java.lang.annotation.Annotation;
16  import java.lang.reflect.InvocationTargetException;
17  import java.lang.reflect.Method;
18  
19  import javax.sql.CommonDataSource;
20  
21  import mockit.internal.injection.InjectionPoint;
22  import mockit.internal.injection.TestedClass;
23  
24  final class TestDataSource {
25      @Nullable
26      private final String dsName;
27      private Class<? extends CommonDataSource> dsClass;
28      private CommonDataSource ds;
29  
30      TestDataSource(@NonNull InjectionPoint injectionPoint) {
31          dsName = injectionPoint.name;
32      }
33  
34      @Nullable
35      CommonDataSource createIfDataSourceDefinitionAvailable(@NonNull TestedClass testedClass) {
36          TestedClass testedClassWithDataSource = testedClass.parent;
37  
38          if (testedClassWithDataSource == null || dsName == null) {
39              return null;
40          }
41  
42          Class<?> testClass = testedClassWithDataSource.testClass;
43  
44          if (testClass != null) {
45              createFromTestedClassOrASuperclass(testClass);
46          }
47  
48          if (ds != null) {
49              return ds;
50          }
51  
52          TestedClass testedClassToBeSearched = testedClassWithDataSource;
53  
54          do {
55              createFromTestedClassOrASuperclass(testedClassToBeSearched.targetClass);
56  
57              if (ds != null) {
58                  return ds;
59              }
60  
61              testedClassToBeSearched = testedClassToBeSearched.parent;
62          } while (testedClassToBeSearched != null);
63  
64          throw new IllegalStateException("Missing @DataSourceDefinition of name \"" + dsName + "\" on "
65                  + testedClassWithDataSource.nameOfTestedClass + " or on a super/parent class");
66      }
67  
68      private void createFromTestedClassOrASuperclass(@NonNull Class<?> targetClass) {
69          do {
70              createDataSource(targetClass);
71  
72              if (ds != null) {
73                  return;
74              }
75  
76              targetClass = targetClass.getSuperclass();
77          } while (targetClass != null && targetClass != Object.class);
78      }
79  
80      private void createDataSource(@NonNull Class<?> targetClass) {
81          for (Annotation annotation : targetClass.getDeclaredAnnotations()) {
82              String annotationName = annotation.annotationType().getName();
83  
84              if ("jakarta.annotation.sql.DataSourceDefinitions".equals(annotationName)) {
85                  createDataSourceJakarta((jakarta.annotation.sql.DataSourceDefinitions) annotation);
86              } else if ("jakarta.annotation.sql.DataSourceDefinition".equals(annotationName)) {
87                  createDataSourceJakarta((jakarta.annotation.sql.DataSourceDefinition) annotation);
88              } else if ("javax.annotation.sql.DataSourceDefinitions".equals(annotationName)) {
89                  createDataSourceJavax((javax.annotation.sql.DataSourceDefinitions) annotation);
90              } else if ("javax.annotation.sql.DataSourceDefinition".equals(annotationName)) {
91                  createDataSourceJavax((javax.annotation.sql.DataSourceDefinition) annotation);
92              }
93  
94              if (ds != null) {
95                  return;
96              }
97          }
98      }
99  
100     private void createDataSourceJakarta(@NonNull jakarta.annotation.sql.DataSourceDefinitions dsDefs) {
101         for (jakarta.annotation.sql.DataSourceDefinition dsDef : dsDefs.value()) {
102             createDataSourceJakarta(dsDef);
103 
104             if (ds != null) {
105                 return;
106             }
107         }
108     }
109 
110     private void createDataSourceJakarta(@NonNull jakarta.annotation.sql.DataSourceDefinition dsDef) {
111         String configuredDataSourceName = InjectionPoint.getNameFromJNDILookup(dsDef.name());
112 
113         if (configuredDataSourceName.equals(dsName)) {
114             instantiateConfiguredDataSourceClassJakarta(dsDef);
115             setDataSourcePropertiesFromConfiguredValuesJakarta(dsDef);
116         }
117     }
118 
119     private void instantiateConfiguredDataSourceClassJakarta(
120             @NonNull jakarta.annotation.sql.DataSourceDefinition dsDef) {
121         String className = dsDef.className();
122 
123         try {
124             // noinspection unchecked
125             dsClass = (Class<? extends CommonDataSource>) Class.forName(className);
126             // noinspection ClassNewInstance
127             ds = dsClass.getDeclaredConstructor().newInstance();
128         } catch (ClassNotFoundException | IllegalAccessException | InstantiationException | IllegalArgumentException
129                 | InvocationTargetException | NoSuchMethodException | SecurityException e) {
130             throw new RuntimeException(e instanceof InstantiationException ? e.getCause() : e);
131         }
132     }
133 
134     private void setDataSourcePropertiesFromConfiguredValuesJakarta(
135             @NonNull jakarta.annotation.sql.DataSourceDefinition dsDef) {
136         try {
137             BeanInfo beanInfo = Introspector.getBeanInfo(dsClass, Object.class);
138             PropertyDescriptor[] properties = beanInfo.getPropertyDescriptors();
139 
140             setProperty(properties, "url", dsDef.url());
141             setProperty(properties, "user", dsDef.user());
142             setProperty(properties, "password", dsDef.password());
143         } catch (IntrospectionException | IllegalAccessException | InvocationTargetException e) {
144             throw new RuntimeException(e);
145         }
146     }
147 
148     private void createDataSourceJavax(@NonNull javax.annotation.sql.DataSourceDefinitions dsDefs) {
149         for (javax.annotation.sql.DataSourceDefinition dsDef : dsDefs.value()) {
150             createDataSourceJavax(dsDef);
151 
152             if (ds != null) {
153                 return;
154             }
155         }
156     }
157 
158     private void createDataSourceJavax(@NonNull javax.annotation.sql.DataSourceDefinition dsDef) {
159         String configuredDataSourceName = InjectionPoint.getNameFromJNDILookup(dsDef.name());
160 
161         if (configuredDataSourceName.equals(dsName)) {
162             instantiateConfiguredDataSourceClassJavax(dsDef);
163             setDataSourcePropertiesFromConfiguredValuesJavax(dsDef);
164         }
165     }
166 
167     private void instantiateConfiguredDataSourceClassJavax(@NonNull javax.annotation.sql.DataSourceDefinition dsDef) {
168         String className = dsDef.className();
169 
170         try {
171             // noinspection unchecked
172             dsClass = (Class<? extends CommonDataSource>) Class.forName(className);
173             // noinspection ClassNewInstance
174             ds = dsClass.getDeclaredConstructor().newInstance();
175         } catch (ClassNotFoundException | IllegalAccessException | InstantiationException | IllegalArgumentException
176                 | InvocationTargetException | NoSuchMethodException | SecurityException e) {
177             throw new RuntimeException(e instanceof InstantiationException ? e.getCause() : e);
178         }
179     }
180 
181     private void setDataSourcePropertiesFromConfiguredValuesJavax(
182             @NonNull javax.annotation.sql.DataSourceDefinition dsDef) {
183         try {
184             BeanInfo beanInfo = Introspector.getBeanInfo(dsClass, Object.class);
185             PropertyDescriptor[] properties = beanInfo.getPropertyDescriptors();
186 
187             setProperty(properties, "url", dsDef.url());
188             setProperty(properties, "user", dsDef.user());
189             setProperty(properties, "password", dsDef.password());
190         } catch (IntrospectionException | IllegalAccessException | InvocationTargetException e) {
191             throw new RuntimeException(e);
192         }
193     }
194 
195     private void setProperty(@NonNull PropertyDescriptor[] properties, @NonNull String name, @NonNull String value)
196             throws InvocationTargetException, IllegalAccessException {
197         for (PropertyDescriptor property : properties) {
198             if (property.getName().equals(name)) {
199                 Method writeMethod = property.getWriteMethod();
200 
201                 if (writeMethod != null) {
202                     writeMethod.invoke(ds, value);
203                 }
204 
205                 return;
206             }
207         }
208     }
209 }