001 /* 002 * Copyright 2011 The Kuali Foundation. 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 package org.kuali.kfs.sys.context; 017 018 import java.io.File; 019 import java.io.FileInputStream; 020 import java.io.FileNotFoundException; 021 import java.io.FileOutputStream; 022 import java.io.InputStream; 023 import java.net.URL; 024 import java.text.MessageFormat; 025 import java.util.ArrayList; 026 import java.util.Collection; 027 import java.util.HashMap; 028 import java.util.HashSet; 029 import java.util.List; 030 import java.util.Map; 031 import java.util.Properties; 032 import java.util.Scanner; 033 import java.util.logging.Level; 034 035 import javax.xml.parsers.DocumentBuilder; 036 import javax.xml.parsers.DocumentBuilderFactory; 037 038 import org.apache.commons.collections.bidimap.TreeBidiMap; 039 import org.apache.commons.lang.StringUtils; 040 import org.apache.cxf.common.logging.LogUtils; 041 import org.apache.cxf.endpoint.ServerImpl; 042 import org.kuali.kfs.sys.KFSConstants; 043 import org.kuali.rice.core.util.ClassLoaderUtils; 044 import org.kuali.rice.kns.datadictionary.AttributeDefinition; 045 import org.kuali.rice.kns.datadictionary.BusinessObjectEntry; 046 import org.kuali.rice.kns.datadictionary.DataDictionary; 047 import org.kuali.rice.kns.datadictionary.DocumentEntry; 048 import org.kuali.rice.kns.datadictionary.InactivationBlockingDefinition; 049 import org.kuali.rice.kns.datadictionary.MaintainableCollectionDefinition; 050 import org.kuali.rice.kns.datadictionary.MaintainableFieldDefinition; 051 import org.kuali.rice.kns.datadictionary.MaintainableItemDefinition; 052 import org.kuali.rice.kns.datadictionary.MaintainableSectionDefinition; 053 import org.kuali.rice.kns.datadictionary.MaintenanceDocumentEntry; 054 import org.kuali.rice.kns.datadictionary.TransactionalDocumentEntry; 055 import org.kuali.rice.kns.datadictionary.control.ControlDefinition; 056 import org.kuali.rice.kns.service.DataDictionaryService; 057 import org.kuali.rice.kns.service.KualiModuleService; 058 import org.kuali.rice.kns.service.ModuleService; 059 import org.springframework.core.io.DefaultResourceLoader; 060 import org.w3c.dom.Document; 061 import org.w3c.dom.Element; 062 import org.w3c.dom.NodeList; 063 064 import uk.ltd.getahead.dwr.impl.DTDEntityResolver; 065 import uk.ltd.getahead.dwr.util.LogErrorHandler; 066 067 public class CheckModularization { 068 069 private static final Map<String, String> OPTIONAL_NAMESPACE_CODES_TO_SPRING_FILE_SUFFIX = new HashMap<String, String>(); 070 static { 071 OPTIONAL_NAMESPACE_CODES_TO_SPRING_FILE_SUFFIX.put("KFS-AR", "ar"); 072 OPTIONAL_NAMESPACE_CODES_TO_SPRING_FILE_SUFFIX.put("KFS-BC", "bc"); 073 OPTIONAL_NAMESPACE_CODES_TO_SPRING_FILE_SUFFIX.put("KFS-CAB", "cab"); 074 OPTIONAL_NAMESPACE_CODES_TO_SPRING_FILE_SUFFIX.put("KFS-CAM", "cam"); 075 OPTIONAL_NAMESPACE_CODES_TO_SPRING_FILE_SUFFIX.put("KFS-CG", "cg"); 076 OPTIONAL_NAMESPACE_CODES_TO_SPRING_FILE_SUFFIX.put("KFS-EC", "ec"); 077 OPTIONAL_NAMESPACE_CODES_TO_SPRING_FILE_SUFFIX.put("KFS-LD", "ld"); 078 OPTIONAL_NAMESPACE_CODES_TO_SPRING_FILE_SUFFIX.put("KFS-PURAP", "purap"); 079 } 080 081 private static final Map<String, String> OPTIONAL_SPRING_FILE_SUFFIX_TO_NAMESPACE_CODES = 082 new TreeBidiMap(OPTIONAL_NAMESPACE_CODES_TO_SPRING_FILE_SUFFIX).inverseBidiMap(); 083 084 private static final Map<String, String> SYSTEM_NAMESPACE_CODES_TO_SPRING_FILE_SUFFIX = new HashMap<String, String>(); 085 static { 086 SYSTEM_NAMESPACE_CODES_TO_SPRING_FILE_SUFFIX.put("KFS-COA", "coa"); 087 SYSTEM_NAMESPACE_CODES_TO_SPRING_FILE_SUFFIX.put("KFS-FP", "fp"); 088 SYSTEM_NAMESPACE_CODES_TO_SPRING_FILE_SUFFIX.put("KFS-GL", "gl"); 089 SYSTEM_NAMESPACE_CODES_TO_SPRING_FILE_SUFFIX.put("KFS-PDP", "pdp"); 090 SYSTEM_NAMESPACE_CODES_TO_SPRING_FILE_SUFFIX.put("KFS-SYS", "sys"); 091 SYSTEM_NAMESPACE_CODES_TO_SPRING_FILE_SUFFIX.put("KFS-VND", "vnd"); 092 } 093 094 private static Map<String,List<String>> PACKAGE_PREFIXES_BY_MODULE = new HashMap<String, List<String>>(); 095 private static Map<String,List<String>> OJB_FILES_BY_MODULE = new HashMap<String, List<String>>(); 096 private static Map<String,List<String>> DWR_FILES_BY_MODULE = new HashMap<String, List<String>>(); 097 098 private static String MODULE_SPRING_PATH_PATTERN = "org/kuali/kfs/module/{0}/spring-{0}.xml"; 099 100 /* 101 * open up classpath:configuration.properties - get locations of spring files? 102 * alter the spring.source.files property and re-save 103 * hold original version and restore? 104 * 105 * use location of config.properties as the class root for scanning source files? 106 * How do you test .class files for symbols? 107 */ 108 static String coreSpringFiles; 109 static String coreSpringTestFiles; 110 static File configPropertiesFile; 111 public static void main(String[] args) { 112 CheckModularization mt = new CheckModularization(); 113 try { 114 Properties configProps = new Properties(); 115 URL propLocation = CheckModularization.class.getClassLoader().getResource("configuration.properties" ); 116 System.out.println( "URL: " + propLocation ); 117 System.out.println( "Path: " + propLocation.getPath() ); 118 configPropertiesFile = new File( propLocation.getPath() ); 119 configProps.load( CheckModularization.class.getClassLoader().getResourceAsStream("configuration.properties") ); 120 coreSpringFiles = configProps.getProperty("core.spring.source.files"); 121 coreSpringTestFiles = configProps.getProperty("core.spring.test.files"); 122 123 LogUtils.getL7dLogger(ServerImpl.class).setLevel(Level.SEVERE); 124 try { 125 SpringContext.initializeTestApplicationContext(); 126 KualiModuleService kualiModuleService = (KualiModuleService)SpringContext.getBean(KualiModuleService.class); 127 128 for ( ModuleService module : kualiModuleService.getInstalledModuleServices() ) { 129 PACKAGE_PREFIXES_BY_MODULE.put(module.getModuleConfiguration().getNamespaceCode(), module.getModuleConfiguration().getPackagePrefixes() ); 130 OJB_FILES_BY_MODULE.put(module.getModuleConfiguration().getNamespaceCode(), module.getModuleConfiguration().getDatabaseRepositoryFilePaths() ); 131 DWR_FILES_BY_MODULE.put(module.getModuleConfiguration().getNamespaceCode(), module.getModuleConfiguration().getScriptConfigurationFilePaths() ); 132 } 133 134 } catch ( Exception ex ) { 135 ex.printStackTrace(); 136 } finally { 137 stopSpringContext(); 138 } 139 140 // bring up Spring once to get all the configuration information, store by namespace code 141 // list of core namespaces, all others must be independent 142 143 // test class references 144 boolean testsPassed = true; 145 System.out.println( "**************************************************"); 146 System.out.println( "Testing Spring Startup"); 147 System.out.println( "**************************************************"); 148 if ( !mt.testSpring() ) { 149 System.out.println( "FAILED" ); 150 testsPassed = false; 151 } else { 152 System.out.println( "SUCCEEDED" ); 153 } 154 System.out.println( "**************************************************"); 155 System.out.println( "Testing OJB References"); 156 System.out.println( "**************************************************"); 157 if ( !mt.testOjb() ) { 158 System.out.println( "FAILED" ); 159 testsPassed = false; 160 } else { 161 System.out.println( "SUCCEEDED" ); 162 } 163 System.out.println( "**************************************************"); 164 System.out.println( "Testing DWR References"); 165 System.out.println( "**************************************************"); 166 if ( !mt.testDwr() ) { 167 System.out.println( "FAILED" ); 168 testsPassed = false; 169 } else { 170 System.out.println( "SUCCEEDED" ); 171 } 172 System.out.println( "**************************************************"); 173 System.out.println( "Testing DD Class References"); 174 System.out.println( "**************************************************"); 175 if ( !mt.testDd() ) { 176 System.out.println( "FAILED" ); 177 testsPassed = false; 178 } else { 179 System.out.println( "SUCCEEDED" ); 180 } 181 // testsPassed &= mt.testDd(); 182 183 if ( !testsPassed ) { 184 System.exit(1); 185 } 186 } 187 catch (Exception e) { 188 e.printStackTrace(); 189 System.exit(1); 190 } 191 System.exit(0); 192 } 193 194 195 protected String buildOptionalModuleSpringFileList( ModuleGroup moduleGroup ) { 196 StringBuffer sb = new StringBuffer(); 197 sb.append( MessageFormat.format(MODULE_SPRING_PATH_PATTERN, OPTIONAL_NAMESPACE_CODES_TO_SPRING_FILE_SUFFIX.get( moduleGroup.namespaceCode ) ) ); 198 for ( String depMod : moduleGroup.optionalModuleDependencyNamespaceCodes ) { 199 sb.append( ',' ); 200 sb.append( MessageFormat.format(MODULE_SPRING_PATH_PATTERN, OPTIONAL_NAMESPACE_CODES_TO_SPRING_FILE_SUFFIX.get( depMod ) ) ); 201 } 202 return sb.toString(); 203 } 204 205 StringBuffer dwrErrorMessage = new StringBuffer("The following optional modules have interdependencies in DWR configuration:"); 206 boolean dwrTestSucceeded = true; 207 StringBuffer ddErrorMessage = new StringBuffer("The following optional modules have interdependencies in DD class references:"); 208 boolean ddTestSucceeded = true; 209 210 public boolean testSpring() throws Exception { 211 boolean testSucceeded = true; 212 StringBuffer errorMessage = new StringBuffer(); 213 // test the core modules alone 214 System.out.println( "\n\n------>Testing for core modules:"); 215 System.out.println( "------>Using Base Configuration: " + coreSpringFiles ); 216 testSucceeded &= testOptionalModuleSpringConfiguration(new ModuleGroup(KFSConstants.ParameterNamespaces.KFS), coreSpringFiles, errorMessage); 217 if ( !testSucceeded ) { 218 errorMessage.insert( 0, "The Core modules have dependencies on the optional modules:\n" ); 219 } 220 221 errorMessage.append( "The following optional modules have interdependencies in Spring configuration:\n"); 222 List<ModuleGroup> optionalModuleGroups = retrieveOptionalModuleGroups(); 223 for (ModuleGroup optionalModuleGroup : optionalModuleGroups) { 224 // if ( !optionalModuleGroup.namespaceCode.equals( "KFS-AR" ) ) continue; 225 System.out.println( "\n\n------>Testing for optional module group: " + optionalModuleGroup ); 226 System.out.println( "------>Using Base Configuration: " + coreSpringFiles ); 227 String moduleConfigFiles = buildOptionalModuleSpringFileList(optionalModuleGroup); 228 System.out.println( "------>Module configuration files: " + moduleConfigFiles ); 229 testSucceeded &= testOptionalModuleSpringConfiguration(optionalModuleGroup, coreSpringFiles+","+moduleConfigFiles, errorMessage); 230 } 231 if (!testSucceeded) { 232 System.out.print(errorMessage.append("\n\n").toString()); 233 } 234 return testSucceeded; 235 } 236 237 protected boolean testOptionalModuleSpringConfiguration(ModuleGroup optionalModuleGroup, String springConfigFiles, StringBuffer errorMessage) { 238 try { 239 // update the configuration.properties file 240 Properties configProps = new Properties(); 241 configProps.load( new FileInputStream( configPropertiesFile ) ); 242 configProps.setProperty( "spring.source.files", springConfigFiles ); 243 configProps.setProperty( "spring.test.files", coreSpringTestFiles ); 244 configProps.setProperty( "validate.ebo.references", "false" ); 245 configProps.store( new FileOutputStream( configPropertiesFile ), "Testing Module: " + optionalModuleGroup.namespaceCode ); 246 configProps.load( new FileInputStream( configPropertiesFile ) ); 247 SpringContext.initializeTestApplicationContext(); 248 dwrTestSucceeded &= testDwrModuleConfiguration(optionalModuleGroup, dwrErrorMessage); 249 ddTestSucceeded &= testDdModuleConfiguration(optionalModuleGroup, ddErrorMessage); 250 return true; 251 } catch (Exception e) { 252 errorMessage.append("\n\n").append(optionalModuleGroup.namespaceCode).append("\n\t").append(e.getMessage()); 253 dwrErrorMessage.append( "\n\n" + optionalModuleGroup.namespaceCode + " : Unable to test due to Spring test failure." ); 254 ddErrorMessage.append( "\n\n" + optionalModuleGroup.namespaceCode + " : Unable to test due to Spring test failure." ); 255 ddTestSucceeded &= false; 256 dwrTestSucceeded &= false; 257 e.printStackTrace(); 258 return false; 259 } 260 finally { 261 stopSpringContext(); 262 } 263 } 264 265 public boolean testOjb() throws Exception { 266 boolean testSucceeded = true; 267 StringBuffer errorMessage = new StringBuffer("The following optional modules have interdependencies in OJB configuration:"); 268 List<ModuleGroup> allModuleGroups = retrieveModuleGroups(); 269 for (ModuleGroup moduleGroup : allModuleGroups) { 270 testSucceeded = testSucceeded & testOptionalModuleOjbConfiguration(moduleGroup, errorMessage); 271 } 272 if (!testSucceeded) { 273 System.out.print(errorMessage.append("\n\n").toString()); 274 } 275 return testSucceeded; 276 } 277 278 protected boolean testOptionalModuleOjbConfiguration(ModuleGroup moduleGroup, StringBuffer errorMessage) throws FileNotFoundException { 279 boolean testSucceeded = true; 280 for (String referencedNamespaceCode : OPTIONAL_NAMESPACE_CODES_TO_SPRING_FILE_SUFFIX.keySet()) { 281 if (!(moduleGroup.namespaceCode.equals(referencedNamespaceCode) || moduleGroup.optionalModuleDependencyNamespaceCodes.contains(referencedNamespaceCode))) { 282 if ( OJB_FILES_BY_MODULE.get(moduleGroup.namespaceCode).isEmpty() ) continue; 283 String firstDatabaseRepositoryFilePath = OJB_FILES_BY_MODULE.get(moduleGroup.namespaceCode).iterator().next(); 284 // the first database repository file path is typically the file that comes shipped with KFS. If institutions override it, this unit test will not test them 285 Scanner scanner = new Scanner(new File("work/src/" + firstDatabaseRepositoryFilePath)); 286 int count = 0; 287 while (scanner.hasNext()) { 288 String token = scanner.next(); 289 String firstPackagePrefix = PACKAGE_PREFIXES_BY_MODULE.get( referencedNamespaceCode ).iterator().next(); 290 // A module may be responsible for many packages, but the first one should be the KFS built-in package that is *not* the module's integration package 291 if (token.contains(firstPackagePrefix)) { 292 count++; 293 } 294 } 295 if (count > 0) { 296 if (testSucceeded) { 297 testSucceeded = false; 298 errorMessage.append("\n").append(moduleGroup.namespaceCode).append(": "); 299 } 300 else { 301 errorMessage.append(", "); 302 } 303 errorMessage.append(count).append(" references to ").append(referencedNamespaceCode); 304 } 305 } 306 } 307 return testSucceeded; 308 } 309 310 protected boolean testDwr() throws Exception { 311 if (!dwrTestSucceeded) { 312 System.out.print(dwrErrorMessage.append("\n\n").toString()); 313 } 314 return dwrTestSucceeded; 315 } 316 317 protected boolean testDwrModuleConfiguration(ModuleGroup moduleGroup, StringBuffer errorMessage) throws Exception { 318 List<String> dwrFiles = DWR_FILES_BY_MODULE.get(moduleGroup.namespaceCode); 319 boolean testSucceeded = true; 320 if (dwrFiles != null && dwrFiles.size() > 0) { 321 // the DWR file delivered with KFS (i.e. the base) should be the first element of the list 322 String baseDwrFileName = dwrFiles.get(0); 323 Document dwrDocument = generateDwrConfigDocument(baseDwrFileName); 324 testSucceeded = testDwrModuleConfiguration(baseDwrFileName, dwrDocument, moduleGroup, errorMessage); 325 } 326 return testSucceeded; 327 } 328 329 protected boolean testDwrModuleConfiguration(String dwrFileName, Document dwrDocument, ModuleGroup moduleGroup, StringBuffer errorMessage) throws Exception { 330 boolean beanClassNamesOK = testDwrBeanClassNames(dwrFileName, dwrDocument, moduleGroup, errorMessage); 331 boolean springServicesOK = testDwrSpringServices(dwrFileName, dwrDocument, moduleGroup, errorMessage); 332 return beanClassNamesOK && springServicesOK; 333 } 334 335 protected boolean testDwrBeanClassNames(String dwrFileName, Document dwrDocument, ModuleGroup moduleGroup, StringBuffer errorMessage) { 336 boolean testSucceeded = true; 337 List<String> dwrBeanClassNames = retrieveDwrBeanClassNames(dwrDocument); 338 for (String referencedNamespaceCode : OPTIONAL_NAMESPACE_CODES_TO_SPRING_FILE_SUFFIX.keySet()) { 339 if (!(referencedNamespaceCode.equals(moduleGroup.namespaceCode) || moduleGroup.optionalModuleDependencyNamespaceCodes.contains(referencedNamespaceCode))) { 340 String firstPackagePrefix = PACKAGE_PREFIXES_BY_MODULE.get(referencedNamespaceCode).iterator().next(); 341 // A module may be responsible for many packages, but the first one should be the KFS built-in package that is *not* the module's integration package 342 if (!firstPackagePrefix.endsWith(".")) { 343 firstPackagePrefix = firstPackagePrefix + "."; 344 } 345 int count = 0; 346 for (String className : dwrBeanClassNames) { 347 if (className.contains(firstPackagePrefix)) { 348 count++; 349 } 350 } 351 if (count > 0) { 352 testSucceeded = false; 353 errorMessage.append("\n\n").append(dwrFileName).append(" (in module ").append(moduleGroup.namespaceCode).append(") has ").append(count).append(" references to business objects from ").append(referencedNamespaceCode); 354 } 355 } 356 } 357 return testSucceeded; 358 } 359 360 protected boolean testDwrSpringServices(String dwrFileName, Document dwrDocument, ModuleGroup moduleGroup, StringBuffer errorMessage) { 361 boolean testSucceeded = true; 362 363 try { 364 List<String> serviceNames = retrieveDwrServiceNames(dwrDocument); 365 for (String serviceName : serviceNames) { 366 try { 367 SpringContext.getBean(serviceName); 368 } catch ( Exception ex ) { 369 testSucceeded = false; 370 errorMessage.append("\n") 371 .append(dwrFileName) 372 .append(" (in module ") 373 .append(moduleGroup.namespaceCode) 374 .append(") has references to spring bean \"") 375 .append(serviceName).append("\" that is not defined in the available spring files"); 376 } 377 } 378 } 379 catch (Exception e) { 380 errorMessage.append("\n").append(moduleGroup.namespaceCode).append("\n\t").append(e.getMessage()); 381 e.printStackTrace(); 382 return testSucceeded = false; 383 } 384 385 return testSucceeded; 386 } 387 388 389 protected Document generateDwrConfigDocument(String fileName) throws Exception { 390 DefaultResourceLoader resourceLoader = new DefaultResourceLoader(ClassLoaderUtils.getDefaultClassLoader()); 391 InputStream in = resourceLoader.getResource(fileName).getInputStream(); 392 393 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); 394 dbf.setValidating(true); 395 396 DocumentBuilder db = dbf.newDocumentBuilder(); 397 db.setEntityResolver(new DTDEntityResolver()); 398 db.setErrorHandler(new LogErrorHandler()); 399 400 Document doc = db.parse(in); 401 return doc; 402 } 403 404 protected List<String> retrieveDwrServiceNames(Document dwrDocument) { 405 List<String> serviceNames = new ArrayList<String>(); 406 // service names are in "create" elements 407 Element root = dwrDocument.getDocumentElement(); 408 NodeList allows = root.getElementsByTagName("allow"); 409 for (int i = 0; i < allows.getLength(); i++) { 410 Element allowElement = (Element) allows.item(i); 411 NodeList creates = allowElement.getElementsByTagName("create"); 412 for (int j = 0; j < creates.getLength(); j++) { 413 Element createElement = (Element) creates.item(j); 414 if ("spring".equals(createElement.getAttribute("creator"))) { 415 NodeList params = createElement.getElementsByTagName("param"); 416 for (int k = 0; k < params.getLength(); k++) { 417 Element paramElement = (Element) params.item(k); 418 if ("beanName".equals(paramElement.getAttribute("name"))) { 419 serviceNames.add(paramElement.getAttribute("value")); 420 } 421 } 422 } 423 424 } 425 } 426 return serviceNames; 427 } 428 429 protected List<String> retrieveDwrBeanClassNames(Document dwrDocument) { 430 List<String> classNames = new ArrayList<String>(); 431 // class names are in "convert" elements 432 Element root = dwrDocument.getDocumentElement(); 433 NodeList allows = root.getElementsByTagName("allow"); 434 for (int i = 0; i < allows.getLength(); i++) { 435 Element allowElement = (Element) allows.item(i); 436 NodeList converts = allowElement.getElementsByTagName("convert"); 437 for (int j = 0; j < converts.getLength(); j++) { 438 Element convertElement = (Element) converts.item(j); 439 if ("bean".equals(convertElement.getAttribute("converter"))) { 440 classNames.add(convertElement.getAttribute("match")); 441 } 442 } 443 } 444 return classNames; 445 } 446 447 protected boolean testDd() throws Exception { 448 if (!ddTestSucceeded) { 449 System.out.print(ddErrorMessage.append("\n\n").toString()); 450 } 451 return ddTestSucceeded; 452 } 453 454 protected boolean testDdModuleConfiguration( ModuleGroup moduleGroup, StringBuffer errorMessage ) { 455 boolean testPassed = true; 456 457 List<String> disallowedPackagesForModule = new ArrayList<String>(); 458 for ( String otherNamespace : PACKAGE_PREFIXES_BY_MODULE.keySet() ) { 459 // if an optional module 460 if ( OPTIONAL_NAMESPACE_CODES_TO_SPRING_FILE_SUFFIX.containsKey( otherNamespace ) ) { 461 // and not the current module 462 if ( !otherNamespace.equals( moduleGroup.namespaceCode ) ) { 463 // add to disallowed list 464 disallowedPackagesForModule.addAll( PACKAGE_PREFIXES_BY_MODULE.get(otherNamespace) ); 465 } 466 } 467 } 468 System.out.println( "---Processing DD for Module: " + moduleGroup.namespaceCode ); 469 System.out.println( "---Disallowed packages: " + disallowedPackagesForModule ); 470 DataDictionary dd = SpringContext.getBean(DataDictionaryService.class).getDataDictionary(); 471 Collection<BusinessObjectEntry> bos = dd.getBusinessObjectEntries().values(); 472 for ( BusinessObjectEntry bo : bos ) { 473 // only check bos for the current module (or all modules if checking the core) 474 if ( ("KFS-SYS".equals( moduleGroup.namespaceCode) 475 || doesPackagePrefixMatch( bo.getFullClassName(), PACKAGE_PREFIXES_BY_MODULE.get( moduleGroup.namespaceCode ) )) 476 && !bo.getFullClassName().startsWith("org.kuali.rice") ) { 477 try { 478 if ( bo.getInactivationBlockingDefinitions() != null ) { 479 for ( InactivationBlockingDefinition ibd : bo.getInactivationBlockingDefinitions() ) { 480 validateDdBusinessObjectClassReference("Invalid Blocked BO Class", ibd.getBlockedBusinessObjectClass(), moduleGroup.namespaceCode, bo.getFullClassName(), null, disallowedPackagesForModule); 481 validateDdBusinessObjectClassReference("Invalid Blocking Reference BO Class", ibd.getBlockingReferenceBusinessObjectClass(), moduleGroup.namespaceCode, bo.getFullClassName(), null, disallowedPackagesForModule); 482 if ( ibd.getInactivationBlockingDetectionServiceBeanName() != null ) { 483 try { 484 SpringContext.getBean( ibd.getInactivationBlockingDetectionServiceBeanName() ); 485 } catch (Exception ex ) { 486 addDdBusinessObjectError("Invalid inactivation blocking service", moduleGroup.namespaceCode, bo.getFullClassName(), null, ibd.getInactivationBlockingDetectionServiceBeanName()); 487 } 488 } 489 } 490 } 491 492 for ( AttributeDefinition ad : bo.getAttributes() ) { 493 try { 494 ControlDefinition cd = ad.getControl(); 495 validateDdBusinessObjectClassReference("Invalid Formatter Class", ad.getFormatterClass(), moduleGroup.namespaceCode, bo.getFullClassName(), ad.getName(), disallowedPackagesForModule); 496 if ( cd != null ) { 497 validateDdBusinessObjectClassReference("Invalid Control Value Finder", cd.getValuesFinderClass(), moduleGroup.namespaceCode, bo.getFullClassName(), ad.getName(), disallowedPackagesForModule); 498 validateDdBusinessObjectClassReference("Invalid BO class for KeyLabelBusinessObjectValueFinder", cd.getBusinessObjectClass(), moduleGroup.namespaceCode, bo.getFullClassName(), ad.getName(), disallowedPackagesForModule); 499 } 500 } catch ( Exception ex ) { 501 addDdBusinessObjectError("Exception Testing BO", moduleGroup.namespaceCode, bo.getFullClassName(), ad.getName(), ex.getClass().getName() + " : " + ex.getMessage() ); 502 System.err.println( "Exception testing BO: " + bo.getFullClassName() + "/" + ad.getName() ); 503 ex.printStackTrace(); 504 testPassed = false; 505 } 506 } 507 } catch( Exception ex ) { 508 addDdBusinessObjectError("Exception Testing BO", moduleGroup.namespaceCode, bo.getFullClassName(), null, ex.getClass().getName() + " : " + ex.getMessage() ); 509 System.err.println( "Exception testing BO: " + bo.getFullClassName() ); 510 ex.printStackTrace(); 511 testPassed = false; 512 } 513 } 514 } 515 516 for ( DocumentEntry de : dd.getDocumentEntries().values() ) { 517 if ( (de instanceof MaintenanceDocumentEntry && ("KFS-SYS".equals( moduleGroup.namespaceCode) || doesPackagePrefixMatch( ((MaintenanceDocumentEntry)de).getBusinessObjectClass().getName(), PACKAGE_PREFIXES_BY_MODULE.get( moduleGroup.namespaceCode )) )) 518 || (de instanceof TransactionalDocumentEntry && ("KFS-SYS".equals( moduleGroup.namespaceCode) || doesPackagePrefixMatch( de.getDocumentClass().getName(), PACKAGE_PREFIXES_BY_MODULE.get( moduleGroup.namespaceCode ))) ) ) { 519 try { 520 if ( de instanceof MaintenanceDocumentEntry ) { 521 MaintenanceDocumentEntry mde = (MaintenanceDocumentEntry)de; 522 validateDdDocumentClassReference("Invalid Maintainable Class", mde.getMaintainableClass(), moduleGroup.namespaceCode, de.getDocumentTypeName(), null, disallowedPackagesForModule); 523 for ( MaintainableSectionDefinition msd : mde.getMaintainableSections() ) { 524 for ( MaintainableItemDefinition mid : msd.getMaintainableItems() ) { 525 if ( mid instanceof MaintainableCollectionDefinition ) { 526 testPassed &= checkMaintainableCollection(moduleGroup.namespaceCode, de.getDocumentTypeName(), (MaintainableCollectionDefinition)mid, disallowedPackagesForModule); 527 } 528 if ( mid instanceof MaintainableFieldDefinition ) { 529 testPassed &= checkMaintainableField( moduleGroup.namespaceCode, de.getDocumentTypeName(), (MaintainableFieldDefinition)mid, disallowedPackagesForModule); 530 } 531 } 532 } 533 } else { // trans doc 534 535 } 536 validateDdDocumentClassReference("Invalid Business Rules Class", de.getBusinessRulesClass(), moduleGroup.namespaceCode, de.getDocumentTypeName(), null, disallowedPackagesForModule); 537 validateDdDocumentClassReference("Invalid DerivedValuesSetterClass", de.getDerivedValuesSetterClass(), moduleGroup.namespaceCode, de.getDocumentTypeName(), null, disallowedPackagesForModule); 538 validateDdDocumentClassReference("Invalid DocumentAuthorizerClass", de.getDocumentAuthorizerClass(), moduleGroup.namespaceCode, de.getDocumentTypeName(), null, disallowedPackagesForModule); 539 validateDdDocumentClassReference("Invalid DocumentPresentationControllerClass", de.getDocumentPresentationControllerClass(), moduleGroup.namespaceCode, de.getDocumentTypeName(), null, disallowedPackagesForModule); 540 validateDdDocumentClassReference("Invalid DocumentSearchGeneratorClass", de.getDocumentSearchGeneratorClass(), moduleGroup.namespaceCode, de.getDocumentTypeName(), null, disallowedPackagesForModule); 541 validateDdDocumentClassReference("Invalid PromptBeforeValidationClass", de.getPromptBeforeValidationClass(), moduleGroup.namespaceCode, de.getDocumentTypeName(), null, disallowedPackagesForModule); 542 } catch ( Exception ex ) { 543 addDdDocumentError("Exception validating Document", moduleGroup.namespaceCode, de.getDocumentTypeName(), null, ex.getClass().getName() + " : " + ex.getMessage() ); 544 System.err.println( "Exception validating Document: " + de.getDocumentTypeName() ); 545 testPassed = false; 546 } 547 } 548 } 549 550 return testPassed; 551 } 552 553 protected void addDdBusinessObjectError( String errorType, String namespaceCode, String businessObjectClassName, String attributeName, String problemClassName ) { 554 ddErrorMessage.append( "\n" ).append( namespaceCode ).append( " - BO: " ); 555 ddErrorMessage.append( businessObjectClassName ); 556 if ( attributeName != null ) { 557 ddErrorMessage.append( " / Attrib: " ).append( attributeName ); 558 } 559 ddErrorMessage.append( " / " ).append( errorType ).append( ": " ).append( problemClassName ); 560 } 561 562 protected boolean validateDdBusinessObjectClassReference( String errorType, String testClassName, String namespaceCode, String businessObjectClassName, String attributeName, List<String> disallowedPackages ) { 563 if (StringUtils.isBlank(testClassName)) { 564 return true; 565 } 566 try { 567 Class<?> testClass = Class.forName(testClassName); 568 return validateDdBusinessObjectClassReference(errorType, testClass, namespaceCode, businessObjectClassName, attributeName, disallowedPackages); 569 } 570 catch (ClassNotFoundException e) { 571 throw new RuntimeException(e); 572 } 573 } 574 575 protected boolean validateDdBusinessObjectClassReference( String errorType, Class<? extends Object> testClass, String namespaceCode, String businessObjectClassName, String attributeName, List<String> disallowedPackages ) { 576 if ( testClass != null ) { 577 if ( doesPackagePrefixMatch(testClass.getName(), disallowedPackages) ) { 578 addDdBusinessObjectError(errorType, namespaceCode, businessObjectClassName, attributeName, testClass.getName()); 579 return false; 580 } 581 } 582 return true; 583 } 584 585 protected void addDdDocumentError( String errorType, String namespaceCode, String documentTypeName, String fieldName, String problemClassName ) { 586 ddErrorMessage.append( "\n" ).append( namespaceCode ).append( " - Doc: " ); 587 ddErrorMessage.append( documentTypeName ); 588 if ( fieldName != null ) { 589 ddErrorMessage.append( " / Field: " ).append( fieldName ); 590 } 591 ddErrorMessage.append( " / " ).append( errorType ).append( ": " ).append( problemClassName ); 592 } 593 594 protected boolean validateDdDocumentClassReference( String errorType, Class<? extends Object> testClass, String namespaceCode, String documentTypeName, String fieldName, List<String> disallowedPackages ) { 595 if ( testClass != null ) { 596 if ( doesPackagePrefixMatch(testClass.getName(), disallowedPackages) ) { 597 addDdDocumentError(errorType, namespaceCode, documentTypeName, fieldName, testClass.getName()); 598 return false; 599 } 600 } 601 return true; 602 } 603 604 protected boolean checkMaintainableCollection( String namespaceCode, String documentTypeName, MaintainableCollectionDefinition collection, List<String> disallowedPackages ) { 605 boolean testPassed = true; 606 testPassed &= validateDdDocumentClassReference("Invalid Collection BO Class", collection.getBusinessObjectClass(), namespaceCode, documentTypeName, collection.getName(), disallowedPackages); 607 testPassed &= validateDdDocumentClassReference("Invalid Collection Source Class", collection.getSourceClassName(), namespaceCode, documentTypeName, collection.getName(), disallowedPackages); 608 for ( MaintainableFieldDefinition mfd : collection.getMaintainableFields() ) { 609 testPassed &= checkMaintainableField( namespaceCode, documentTypeName, mfd, disallowedPackages); 610 } 611 for ( MaintainableCollectionDefinition mcd : collection.getMaintainableCollections() ) { 612 testPassed &= checkMaintainableCollection( namespaceCode, documentTypeName, mcd, disallowedPackages); 613 } 614 615 return testPassed; 616 } 617 protected boolean checkMaintainableField( String namespaceCode, String documentTypeName, MaintainableFieldDefinition field, List<String> disallowedPackages ) { 618 boolean testPassed = true; 619 try { 620 testPassed &= validateDdDocumentClassReference("Invalid Default Value Finder Class", field.getDefaultValueFinderClass(), namespaceCode, documentTypeName, field.getName(), disallowedPackages); 621 testPassed &= validateDdDocumentClassReference("Invalid Override Lookup Class", field.getOverrideLookupClass(), namespaceCode, documentTypeName, field.getName(), disallowedPackages); 622 } catch ( Exception ex ) { 623 addDdDocumentError("Exception validating Maint Doc Field", namespaceCode, documentTypeName, field.getName(), ex.getClass().getName() + " : " + ex.getMessage() ); 624 System.err.println( "Exception validating Maint Doc Field: " + documentTypeName + "/" + field.getName() ); 625 ex.printStackTrace(); 626 testPassed = false; 627 } 628 return testPassed; 629 } 630 631 protected boolean doesPackagePrefixMatch( String className, List<String> packagePrefixList ) { 632 for ( String pkg : packagePrefixList ) { 633 if ( className.startsWith(pkg) ) { 634 return true; 635 } 636 } 637 return false; 638 } 639 640 public class ModuleGroup { 641 public String namespaceCode; 642 public HashSet<String> optionalModuleDependencyNamespaceCodes = new HashSet<String>(); 643 644 public ModuleGroup() { 645 // TODO Auto-generated constructor stub 646 } 647 648 public ModuleGroup( String namespaceCode ) { 649 this.namespaceCode = namespaceCode; 650 } 651 652 @Override 653 public String toString() { 654 return namespaceCode + (optionalModuleDependencyNamespaceCodes.isEmpty()?"":(" - depends on: " + optionalModuleDependencyNamespaceCodes)); 655 } 656 } 657 658 public List<ModuleGroup> retrieveModuleGroups() throws Exception { 659 List<ModuleGroup> moduleGroups = new ArrayList<ModuleGroup>(); 660 661 for (String systemNamespaceCode : SYSTEM_NAMESPACE_CODES_TO_SPRING_FILE_SUFFIX.keySet()) { 662 ModuleGroup systemModuleGroup = new ModuleGroup(); 663 systemModuleGroup.namespaceCode = systemNamespaceCode; 664 moduleGroups.add(systemModuleGroup); 665 } 666 667 moduleGroups.addAll(retrieveOptionalModuleGroups()); 668 669 return moduleGroups; 670 } 671 672 public List<ModuleGroup> retrieveOptionalModuleGroups() throws Exception { 673 Document designXmlDocument = getDesignXmlDocument(); 674 List<Element> optionalModuleDefinitions = retrieveOptionalModuleDefinitions(designXmlDocument); 675 List<ModuleGroup> optionalModuleGroups = new ArrayList<ModuleGroup>(); 676 677 for (Element optionalModuleDefinition : optionalModuleDefinitions) { 678 ModuleGroup optionalModuleGroup = buildOptionalModuleGroup(optionalModuleDefinition); 679 if (optionalModuleGroup != null) { 680 optionalModuleGroups.add(optionalModuleGroup); 681 } 682 } 683 684 return optionalModuleGroups; 685 } 686 687 public Document getDesignXmlDocument() throws Exception { 688 DefaultResourceLoader resourceLoader = new DefaultResourceLoader(ClassLoaderUtils.getDefaultClassLoader()); 689 InputStream in = resourceLoader.getResource(DefaultResourceLoader.CLASSPATH_URL_PREFIX + "design.xml").getInputStream(); 690 691 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); 692 693 DocumentBuilder db = dbf.newDocumentBuilder(); 694 695 Document doc = db.parse(in); 696 return doc; 697 } 698 699 public List<Element> retrieveOptionalModuleDefinitions(Document designXmlDocument) throws Exception { 700 List<Element> optionalModuleDefinitions = new ArrayList<Element>(); 701 Element root = designXmlDocument.getDocumentElement(); 702 703 // in the design.xml file, an optional module/package is specified by a <package> tag that does not have the needdeclarations attribute equal false 704 NodeList packages = root.getElementsByTagName("package"); 705 for (int i = 0; i < packages.getLength(); i++) { 706 Element packageElement = (Element) packages.item(i); 707 if (!"false".equals(packageElement.getAttribute("needdeclarations"))) { 708 optionalModuleDefinitions.add(packageElement); 709 } 710 } 711 return optionalModuleDefinitions; 712 } 713 714 public ModuleGroup buildOptionalModuleGroup(Element optionalPackageElement) { 715 ModuleGroup moduleGroup = null; 716 if (OPTIONAL_SPRING_FILE_SUFFIX_TO_NAMESPACE_CODES.containsKey(optionalPackageElement.getAttribute("name"))) { 717 moduleGroup = new ModuleGroup(); 718 moduleGroup.namespaceCode = OPTIONAL_SPRING_FILE_SUFFIX_TO_NAMESPACE_CODES.get(optionalPackageElement.getAttribute("name")); 719 if (StringUtils.isNotBlank(optionalPackageElement.getAttribute("depends"))) { 720 if (OPTIONAL_SPRING_FILE_SUFFIX_TO_NAMESPACE_CODES.containsKey(optionalPackageElement.getAttribute("depends"))) { 721 moduleGroup.optionalModuleDependencyNamespaceCodes.add(OPTIONAL_SPRING_FILE_SUFFIX_TO_NAMESPACE_CODES.get(optionalPackageElement.getAttribute("depends"))); 722 } 723 } 724 NodeList dependsElements = optionalPackageElement.getElementsByTagName("depends"); 725 for (int i = 0; i < dependsElements.getLength(); i++) { 726 Element dependsElement = (Element) dependsElements.item(i); 727 if (OPTIONAL_SPRING_FILE_SUFFIX_TO_NAMESPACE_CODES.containsKey(StringUtils.trim(dependsElement.getTextContent()))) { 728 moduleGroup.optionalModuleDependencyNamespaceCodes.add(OPTIONAL_SPRING_FILE_SUFFIX_TO_NAMESPACE_CODES.get(StringUtils.trim(dependsElement.getTextContent()))); 729 } 730 } 731 } 732 return moduleGroup; 733 } 734 735 protected static void stopSpringContext() { 736 try { 737 SpringContext.close(); 738 } catch (Exception e) { 739 System.out.println("Caught exception shutting down spring"); 740 e.printStackTrace(); 741 } 742 } 743 }