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 }