1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.kuali.kfs.sys.context;
20
21 import java.lang.reflect.Field;
22 import java.sql.Connection;
23 import java.sql.ResultSet;
24 import java.sql.Statement;
25 import java.util.ArrayList;
26 import java.util.HashMap;
27 import java.util.HashSet;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.Set;
31 import java.util.TreeSet;
32
33 import javax.sql.DataSource;
34
35 import org.apache.commons.lang.StringUtils;
36 import org.apache.log4j.Logger;
37 import org.kuali.kfs.sys.ConfigureContext;
38 import org.kuali.kfs.sys.document.datadictionary.FinancialSystemMaintenanceDocumentEntry;
39 import org.kuali.kfs.sys.suite.AnnotationTestSuite;
40 import org.kuali.kfs.sys.suite.PreCommitSuite;
41 import org.kuali.rice.core.api.mo.common.active.MutableInactivatable;
42 import org.kuali.rice.kns.datadictionary.BusinessObjectEntry;
43 import org.kuali.rice.kns.datadictionary.InquiryDefinition;
44 import org.kuali.rice.kns.datadictionary.InquirySectionDefinition;
45 import org.kuali.rice.kns.datadictionary.LookupDefinition;
46 import org.kuali.rice.kns.datadictionary.MaintainableSectionDefinition;
47 import org.kuali.rice.krad.datadictionary.AttributeDefinition;
48 import org.kuali.rice.krad.datadictionary.DataDictionary;
49 import org.kuali.rice.krad.datadictionary.DocumentEntry;
50 import org.kuali.rice.krad.service.DataDictionaryService;
51 import org.springframework.beans.factory.NoSuchBeanDefinitionException;
52 import org.springframework.beans.factory.config.BeanDefinition;
53 import org.springframework.beans.factory.support.KualiDefaultListableBeanFactory;
54
55 @AnnotationTestSuite(PreCommitSuite.class)
56 @ConfigureContext
57 public class DataDictionaryConfigurationTest extends KualiTestBase {
58 private static final Logger LOG = Logger.getLogger(DataDictionaryConfigurationTest.class);
59 private DataDictionary dataDictionary;
60
61 public final static String KFS_PACKAGE_NAME_PREFIX = "org.kuali.kfs";
62 public final static String BUSINESS_OBJECT_PATH_QUALIFIER = "businessobject/datadictionary";
63 public final static String DOCUMENT_PATH_QUALIFIER = "document/datadictionary";
64 public final static String RICE_PACKAGE_NAME_PREFIX = "org.kuali.rice";
65 public final static String INACTIVATEABLE_INTERFACE_CLASS = MutableInactivatable.class.getName();
66 public final static String ACTIVE_FIELD_NAME = "active";
67
68 public void testAllDataDictionaryDocumentTypesExistInWorkflowDocumentTypeTable() throws Exception {
69 HashSet<String> workflowDocumentTypeNames = new HashSet<String>();
70 DataSource mySource = SpringContext.getBean(DataSource.class);
71 Connection dbCon = null;
72 try {
73
74 dbCon = mySource.getConnection();
75 Statement dbAsk = dbCon.createStatement();
76 ResultSet dbAnswer = dbAsk.executeQuery("select DOC_TYP_NM from KREW_DOC_TYP_T where CUR_IND = 1");
77 while (dbAnswer.next()) {
78 String docName = dbAnswer.getString(1);
79 if (StringUtils.isNotBlank(docName)) {
80 workflowDocumentTypeNames.add(docName);
81 }
82 }
83
84 }
85 catch (Exception e) {
86 throw (e);
87 }
88
89 HashSet<DocumentEntry> documentEntries = new HashSet<DocumentEntry>(dataDictionary.getDocumentEntries().values());
90 List<String> ddEntriesWithMissingTypes = new ArrayList<String>();
91 for (DocumentEntry documentEntry : documentEntries) {
92 String name = documentEntry.getDocumentTypeName();
93 String testName = new String(" ");
94 if (documentEntry instanceof FinancialSystemMaintenanceDocumentEntry){
95 testName=((FinancialSystemMaintenanceDocumentEntry)documentEntry).getBusinessObjectClass().getName();
96 }else{
97 testName=documentEntry.getDocumentClass().getName();
98 }
99 if (!workflowDocumentTypeNames.contains(name) && !"RiceUserMaintenanceDocument".equals(name) && !testName.contains("rice")) {
100 ddEntriesWithMissingTypes.add(name);
101 }
102 else {
103 workflowDocumentTypeNames.remove(name);
104 }
105 }
106
107 if (workflowDocumentTypeNames.size() > 0) {
108 try{
109
110 String queryString = "select distinct doc_typ_nm from KREW_DOC_TYP_T"
111 +" where doc_typ_id in (select parnt_id from KREW_DOC_TYP_T"
112 +" where actv_ind = 1"
113 +" and cur_ind = 1)";
114 Statement dbAsk = dbCon.createStatement();
115 ResultSet dbAnswer = dbAsk.executeQuery(queryString);
116 while (dbAnswer.next()) {
117 String docName = dbAnswer.getString(1);
118 if (StringUtils.isNotBlank(docName)) {
119 workflowDocumentTypeNames.remove(docName);
120 }
121 }
122 }catch (Exception e){
123 throw (e);
124 }
125
126 System.err.print("superfluousTypesDefinedInWorkflowDatabase: " + workflowDocumentTypeNames);
127 }
128 assertEquals("documentTypesNotDefinedInWorkflowDatabase: " + ddEntriesWithMissingTypes, 0, ddEntriesWithMissingTypes.size());
129 }
130
131 private final static List<String> INACTIVATEABLE_LOOKUP_IGNORE_CLASSES = new ArrayList<String>();
132 static {
133
134 INACTIVATEABLE_LOOKUP_IGNORE_CLASSES.add( "org.kuali.kfs.coa.businessobject.Account" );
135 INACTIVATEABLE_LOOKUP_IGNORE_CLASSES.add( "org.kuali.kfs.module.bc.businessobject.BudgetConstructionPosition" );
136 INACTIVATEABLE_LOOKUP_IGNORE_CLASSES.add( "org.kuali.kfs.module.bc.businessobject.PendingBudgetConstructionAppointmentFunding" );
137 }
138 private static final List<String> INACTIVATEABLE_LOOKUP_IGNORE_PACKAGES = new ArrayList<String>();
139 static {
140 INACTIVATEABLE_LOOKUP_IGNORE_PACKAGES.add( "org.kuali.kfs.pdp.businessobject" );
141 INACTIVATEABLE_LOOKUP_IGNORE_PACKAGES.add( "org.kuali.kfs.module.external.kc.businessobject" );
142 }
143
144 public void testActiveFieldExistInLookupAndResultSection() throws Exception{
145 List<Class<?>> noActiveFieldClassList = new ArrayList<Class<?>>();
146 List<Class<?>> notImplementInactivatableList = new ArrayList<Class<?>>();
147 List<Class<?>> defaultValueWrongList = new ArrayList<Class<?>>();
148
149 for(org.kuali.rice.krad.datadictionary.BusinessObjectEntry kradBusinessObjectEntry:dataDictionary.getBusinessObjectEntries().values()){
150 BusinessObjectEntry businessObjectEntry = (BusinessObjectEntry) kradBusinessObjectEntry;
151 if ( !businessObjectEntry.getBusinessObjectClass().getName().startsWith(RICE_PACKAGE_NAME_PREFIX)
152 && !INACTIVATEABLE_LOOKUP_IGNORE_CLASSES.contains(businessObjectEntry.getBusinessObjectClass().getName())
153 && !INACTIVATEABLE_LOOKUP_IGNORE_PACKAGES.contains(businessObjectEntry.getBusinessObjectClass().getPackage().getName()) ) {
154 try {
155 LookupDefinition lookupDefinition = businessObjectEntry.getLookupDefinition();
156
157 if(Class.forName(INACTIVATEABLE_INTERFACE_CLASS).isAssignableFrom(businessObjectEntry.getBusinessObjectClass())) {
158 if(lookupDefinition != null && !(lookupDefinition.getLookupFieldNames().contains(ACTIVE_FIELD_NAME) && lookupDefinition.getResultFieldNames().contains(ACTIVE_FIELD_NAME))){
159 noActiveFieldClassList.add(businessObjectEntry.getBusinessObjectClass());
160 if ( lookupDefinition.getLookupField(ACTIVE_FIELD_NAME) != null ) {
161
162 if (!StringUtils.equals(lookupDefinition.getLookupField(ACTIVE_FIELD_NAME).getDefaultValue(), "Y")) {
163 defaultValueWrongList.add(businessObjectEntry.getBusinessObjectClass());
164 }
165 }
166 }
167 }else{
168
169 if(lookupDefinition != null && (lookupDefinition.getLookupFieldNames().contains(ACTIVE_FIELD_NAME) || lookupDefinition.getResultFieldNames().contains(ACTIVE_FIELD_NAME))){
170 notImplementInactivatableList.add(businessObjectEntry.getBusinessObjectClass());
171 }
172 }
173 }
174 catch (ClassNotFoundException e) {
175 throw(e);
176 }
177 }
178 }
179 String errorString = "";
180 if (noActiveFieldClassList.size()!=0) errorString=errorString+"Missing Active Field: "+formatErrorStringGroupByModule(noActiveFieldClassList);
181 if (notImplementInactivatableList.size()!=0) errorString=errorString+"Inactivatable not implemented: "+formatErrorStringGroupByModule(notImplementInactivatableList);
182 if (defaultValueWrongList.size()!=0) errorString=errorString+"Wrong default value: "+formatErrorStringGroupByModule(defaultValueWrongList);
183 assertEquals(errorString, 0, noActiveFieldClassList.size()+notImplementInactivatableList.size()+defaultValueWrongList.size());
184 }
185 private String formatErrorStringGroupByModule(List<Class<?>> failedList){
186 Map<String,Set<String>> listMap = new HashMap<String, Set<String>>();
187 String module = null;
188 String itemName = null;
189 for (Class<?> item :failedList){
190 itemName=item.getName();
191 module = itemName.substring(0, itemName.lastIndexOf('.'));
192 if (!listMap.keySet().contains(module)){
193 listMap.put(module, new HashSet<String>());
194 }
195 listMap.get(module).add(itemName.substring(itemName.lastIndexOf('.')+1));
196 }
197 String tempString="";
198 for (String moduleName : listMap.keySet()){
199 tempString = tempString+"Module :"+moduleName+"\n";
200 for (String errorClass : (Set<String>)listMap.get(moduleName)){
201 tempString = tempString + " "+errorClass+"\n";
202 }
203 }
204 return "\n"+tempString;
205 }
206
207 public void testAllBusinessObjectsHaveObjectLabel() throws Exception {
208 List<Class<?>> noObjectLabelClassList = new ArrayList<Class<?>>();
209 for(org.kuali.rice.krad.datadictionary.BusinessObjectEntry kradBusinessObjectEntry:dataDictionary.getBusinessObjectEntries().values()){
210 BusinessObjectEntry businessObjectEntry = (BusinessObjectEntry) kradBusinessObjectEntry;
211 if (StringUtils.isBlank(businessObjectEntry.getObjectLabel())) {
212 noObjectLabelClassList.add(businessObjectEntry.getBusinessObjectClass());
213 }
214 }
215 assertEquals(noObjectLabelClassList.toString(), 0, noObjectLabelClassList.size());
216 }
217
218 public void testAllParentBeansAreAbstract() throws Exception {
219 Field f = dataDictionary.getClass().getDeclaredField("ddBeans");
220 f.setAccessible(true);
221 KualiDefaultListableBeanFactory ddBeans = (KualiDefaultListableBeanFactory)f.get(dataDictionary);
222 List<String> failingBeanNames = new ArrayList<String>();
223 for ( String beanName : ddBeans.getBeanDefinitionNames() ) {
224 BeanDefinition beanDef = ddBeans.getMergedBeanDefinition(beanName);
225 String beanClass = beanDef.getBeanClassName();
226
227 if ( beanClass != null && beanClass.startsWith("org.kuali.rice") ) {
228 continue;
229 }
230 if ( (beanName.endsWith("-parentBean") || beanName.endsWith("-baseBean"))
231 && !beanDef.isAbstract() ) {
232 failingBeanNames.add(beanName + " : " + beanDef.getResourceDescription()+"\n");
233 }
234 }
235 assertEquals( "The following parent beans are not defined as abstract:\n" + failingBeanNames, 0, failingBeanNames.size() );
236 }
237
238 public void testBusinessObjectEntriesShouldHaveParentBeans() throws Exception {
239 somethingShouldHaveParentBeans(BusinessObjectEntry.class, new ArrayList<String>());
240 }
241
242 public void testDocumentEntriesShouldHaveParentBeans() throws Exception {
243 somethingShouldHaveParentBeans(DocumentEntry.class, new ArrayList<String>());
244 }
245
246 protected static final List<String> EXCLUDED_ATTRIBUTE_DEFINITIONS = new ArrayList<String>();
247 static {
248 EXCLUDED_ATTRIBUTE_DEFINITIONS.add( "Country-" );
249 EXCLUDED_ATTRIBUTE_DEFINITIONS.add( "County-" );
250 EXCLUDED_ATTRIBUTE_DEFINITIONS.add( "State-" );
251 EXCLUDED_ATTRIBUTE_DEFINITIONS.add( "PostalCode-" );
252 EXCLUDED_ATTRIBUTE_DEFINITIONS.add( "PersonImpl-" );
253 EXCLUDED_ATTRIBUTE_DEFINITIONS.add( "RoleMemberBo-" );
254 EXCLUDED_ATTRIBUTE_DEFINITIONS.add( "KimAttributes-" );
255 EXCLUDED_ATTRIBUTE_DEFINITIONS.add( "KimDocRoleMember-" );
256 EXCLUDED_ATTRIBUTE_DEFINITIONS.add( "DocRoleMember-" );
257 EXCLUDED_ATTRIBUTE_DEFINITIONS.add( "Responsibility-" );
258 EXCLUDED_ATTRIBUTE_DEFINITIONS.add( "PermissionBo-" );
259 EXCLUDED_ATTRIBUTE_DEFINITIONS.add( "PermissionImpl-" );
260 EXCLUDED_ATTRIBUTE_DEFINITIONS.add( "UberPermission-" );
261 EXCLUDED_ATTRIBUTE_DEFINITIONS.add( "ReviewResponsibility-" );
262 EXCLUDED_ATTRIBUTE_DEFINITIONS.add( "ResponsibilityImpl-" );
263 EXCLUDED_ATTRIBUTE_DEFINITIONS.add( "UberPermissionBo-" );
264 EXCLUDED_ATTRIBUTE_DEFINITIONS.add( "RuleTemplateAttribute-" );
265 EXCLUDED_ATTRIBUTE_DEFINITIONS.add( "-versionNumber" );
266 }
267
268 public void testAttributeDefinitionsShouldHaveParentBeans() throws Exception {
269 somethingShouldHaveParentBeans(AttributeDefinition.class, EXCLUDED_ATTRIBUTE_DEFINITIONS);
270 }
271
272 public void testMaintenanceSectionsShouldHaveParentBeans() throws Exception {
273 somethingShouldHaveParentBeans(MaintainableSectionDefinition.class, new ArrayList<String>());
274 }
275
276 public void testInquirySectionsShouldHaveParentBeans() throws Exception {
277 somethingShouldHaveParentBeans(InquirySectionDefinition.class, new ArrayList<String>());
278 }
279
280 public void testLookupDefinitionsShouldHaveParentBeans() throws Exception {
281 somethingShouldHaveParentBeans(LookupDefinition.class, new ArrayList<String>());
282 }
283
284 public void testInquiryDefinitionsShouldHaveParentBeans() throws Exception {
285 somethingShouldHaveParentBeans(InquiryDefinition.class, new ArrayList<String>() );
286 }
287
288 protected boolean doesBeanNameMatchList( String beanName, List<String> exclusions ) {
289 for ( String excl : exclusions ) {
290 if ( beanName.contains(excl) ) {
291 return true;
292 }
293 }
294 return false;
295 }
296
297 protected void somethingShouldHaveParentBeans( Class<?> baseClass, List<String> exclusions ) throws Exception {
298 Field f = dataDictionary.getClass().getDeclaredField("ddBeans");
299 f.setAccessible(true);
300 KualiDefaultListableBeanFactory ddBeans = (KualiDefaultListableBeanFactory)f.get(dataDictionary);
301 List<String> failingBeanNames = new ArrayList<String>();
302
303 for ( String beanName : ddBeans.getBeanDefinitionNames() ) {
304 if ( doesBeanNameMatchList(beanName, exclusions)) {
305 continue ;
306 }
307 BeanDefinition beanDef = ddBeans.getMergedBeanDefinition(beanName);
308 String beanClass = beanDef.getBeanClassName();
309 if ( beanClass == null ) {
310 System.err.println( "ERROR: Bean " + beanName + " has a null class." );
311 }
312 if ( !beanDef.isAbstract()
313 && beanClass != null
314 && baseClass.isAssignableFrom(Class.forName(beanClass) ) ) {
315 try {
316 BeanDefinition parentBean = ddBeans.getBeanDefinition(beanName + "-parentBean");
317 } catch ( NoSuchBeanDefinitionException ex ) {
318 failingBeanNames.add(beanName + " : " + beanDef.getResourceDescription() +"\n");
319 }
320 }
321 }
322 assertEquals( "The following " + baseClass.getSimpleName() + " beans do not have \"-parentBean\"s:\n" + failingBeanNames, 0, failingBeanNames.size() );
323 }
324
325 private void reportErrorAttribute(Map<String, Set<String>> reports, AttributeDefinition attributeDefinition, String boClassName) {
326 Set<String> attributeSet = reports.containsKey(boClassName) ? reports.get(boClassName) : new TreeSet<String>();
327 attributeSet.add(attributeDefinition.getName());
328 reports.put(boClassName, attributeSet);
329 }
330
331 private StringBuilder convertReportsAsText(Map<String, Set<String>> reports) {
332 StringBuilder reportsAsText = new StringBuilder();
333 for(String key : new TreeSet<String>(reports.keySet())) {
334 reportsAsText.append(key + "\n");
335 for(String value : reports.get(key)) {
336 reportsAsText.append("\t--").append(value).append("\n");
337 }
338 }
339 return reportsAsText;
340 }
341
342 private void printReport(Map<String, Set<String>> reports) {
343 StringBuilder reportsAsText = convertReportsAsText(reports);
344 System.out.println(reportsAsText);
345 LOG.info("\n" + reportsAsText);
346 }
347
348 @Override
349 protected void setUp() throws Exception {
350 super.setUp();
351 dataDictionary = SpringContext.getBean(DataDictionaryService.class).getDataDictionary();
352 }
353 }