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