/*
* The Kuali Financial System, a comprehensive financial management system for higher education.
*
* Copyright 2005-2014 The Kuali Foundation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.kuali.kfs.sys.context;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang.StringUtils;
import org.kuali.kfs.sys.ConfigureContext;
import org.kuali.kfs.sys.FinancialSystemModuleConfiguration;
import org.kuali.kfs.sys.batch.JobDescriptor;
import org.kuali.kfs.sys.batch.TriggerDescriptor;
import org.kuali.kfs.sys.batch.dataaccess.FiscalYearMaker;
import org.kuali.kfs.sys.batch.dataaccess.impl.FiscalYearMakerImpl;
import org.kuali.kfs.sys.service.impl.KfsModuleServiceImpl;
import org.kuali.kfs.sys.suite.AnnotationTestSuite;
import org.kuali.kfs.sys.suite.PreCommitSuite;
import org.kuali.rice.core.framework.persistence.dao.PlatformAwareDao;
import org.kuali.rice.core.framework.persistence.jdbc.dao.PlatformAwareDaoBaseJdbc;
import org.kuali.rice.core.framework.persistence.ojb.dao.PlatformAwareDaoBaseOjb;
import org.kuali.rice.kns.lookup.Lookupable;
import org.kuali.rice.kns.lookup.LookupableHelperService;
import org.kuali.rice.krad.service.ModuleService;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.BeanDefinition;
@ConfigureContext
@AnnotationTestSuite(PreCommitSuite.class)
public class SpringConfigurationConsistencyCheckTest extends KualiTestBase {
private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(SpringConfigurationConsistencyCheckTest.class);
public void testAllLookupablesArePrototypes() throws Exception {
List<String> failingBeans = new ArrayList<String>();
Map<String,Lookupable> beans = SpringContext.getBeansOfType(Lookupable.class);
Map<String,Lookupable> beans2 = SpringContext.getBeansOfType(Lookupable.class);
for ( String beanName : beans.keySet() ) {
BeanDefinition beanDef = SpringContext.applicationContext.getBeanFactory().getBeanDefinition(beanName);
// skip entries in the rice import files or in testing files
if ( StringUtils.contains( beanDef.getResourceDescription(), "spring-kfs-imported-rice-beans.xml" )
|| StringUtils.contains( beanDef.getResourceDescription(), "-test.xml" ) ) {
continue;
}
if ( ProxyUtils.getTargetIfProxied( beans.get(beanName) ).equals(ProxyUtils.getTargetIfProxied( beans2.get(beanName) )) ) {
failingBeans.add( "\n *** " + beanName + " is a singleton and should not be." );
}
}
assertEquals( "Beans Failing Non-Singleton check: " + failingBeans, 0, failingBeans.size() );
}
public void testAllLookupableHelperServicesArePrototypes() throws Exception {
List<String> failingBeans = new ArrayList<String>();
Map<String,LookupableHelperService> beans = SpringContext.getBeansOfType(LookupableHelperService.class);
Map<String,LookupableHelperService> beans2 = SpringContext.getBeansOfType(LookupableHelperService.class);
for ( String beanName : beans.keySet() ) {
BeanDefinition beanDef = SpringContext.applicationContext.getBeanFactory().getBeanDefinition(beanName);
// skip entries in the rice import files or in testing files
if ( StringUtils.contains( beanDef.getResourceDescription(), "spring-kfs-imported-rice-beans.xml" )
|| StringUtils.contains( beanDef.getResourceDescription(), "-test.xml" ) ) {
continue;
}
if ( ProxyUtils.getTargetIfProxied( beans.get(beanName) ).equals(ProxyUtils.getTargetIfProxied( beans2.get(beanName) )) ) {
failingBeans.add( "\n *** " + beanName + " is a singleton and should not be. (" + beanDef.getResourceDescription() + ")" );
}
}
assertEquals( "Beans Failing Non-Singleton check: " + failingBeans, 0, failingBeans.size() );
}
public void testJobBeansReferencedInModuleDefinitions() throws Exception {
// get all Job beans
Map<String,JobDescriptor> jobs = SpringContext.getBeansOfType(JobDescriptor.class);
Map<String,ModuleService> moduleServices = SpringContext.getBeansOfType(ModuleService.class);
assertFalse( "Jobs list must not be empty", jobs.isEmpty() );
assertFalse( "Module list must not be empty", moduleServices.isEmpty() );
// build a list of all job names
Set<String> jobNamesInBeans = jobs.keySet();
Set<String> jobNamesInModules = new HashSet<String>();
for ( ModuleService m : moduleServices.values() ) {
jobNamesInModules.addAll( m.getModuleConfiguration().getJobNames() );
}
Set<String> beansNotReferencedInModules = new HashSet<String>( jobNamesInBeans );
beansNotReferencedInModules.removeAll(jobNamesInModules);
Set<String> moduleJobsWithNoBeans = new HashSet<String>( jobNamesInModules );
moduleJobsWithNoBeans.removeAll(jobNamesInBeans);
assertEquals( "All of the job definitions must be referenced in the module definitions.", Collections.emptySet(), beansNotReferencedInModules );
assertEquals( "All of the jobs in the module definitions must be defined in the Spring context.", Collections.emptySet(), moduleJobsWithNoBeans );
}
public void testTriggerBeansReferencedInModuleDefinitions() throws Exception {
// get all Job beans
Map<String,TriggerDescriptor> triggers = SpringContext.getBeansOfType(TriggerDescriptor.class);
Map<String,ModuleService> moduleServices = SpringContext.getBeansOfType(ModuleService.class);
assertFalse( "Jobs list must not be empty", triggers.isEmpty() );
assertFalse( "Module list must not be empty", moduleServices.isEmpty() );
// build a list of all job names
Set<String> triggerNamesInBeans = triggers.keySet();
Set<String> triggerNamesInModules = new HashSet<String>();
for ( ModuleService m : moduleServices.values() ) {
triggerNamesInModules.addAll( m.getModuleConfiguration().getTriggerNames() );
}
Set<String> beansNotReferencedInModules = new HashSet<String>( triggerNamesInBeans );
beansNotReferencedInModules.removeAll(triggerNamesInModules);
Set<String> moduleTriggersWithNoBeans = new HashSet<String>( triggerNamesInModules );
moduleTriggersWithNoBeans.removeAll(triggerNamesInBeans);
assertEquals( "All of the trigger definitions must be referenced in the module definitions.", Collections.emptySet(), beansNotReferencedInModules );
assertEquals( "All of the triggers in the module definitions must be defined in the Spring context.", Collections.emptySet(), moduleTriggersWithNoBeans );
}
// FY makers referenced in module definitions
public void testFiscalYearMakerBeansReferencedInModuleDefinitions() throws Exception {
// get all Job beans
Map<String,FiscalYearMakerImpl> fiscalYearMakers = SpringContext.getBeansOfType(FiscalYearMakerImpl.class);
Map<String,KfsModuleServiceImpl> moduleServices = SpringContext.getBeansOfType(KfsModuleServiceImpl.class);
assertFalse( "FiscalYearMaker list must not be empty", fiscalYearMakers.isEmpty() );
assertFalse( "Module list must not be empty", moduleServices.isEmpty() );
// build a list of all job names
Set<FiscalYearMakerImpl> fiscalYearMakerNamesInBeans = new HashSet<FiscalYearMakerImpl>();
for ( FiscalYearMakerImpl fym : fiscalYearMakers.values() ) {
fiscalYearMakerNamesInBeans.add( (FiscalYearMakerImpl)ProxyUtils.getTargetIfProxied(fym) );
}
Set<FiscalYearMakerImpl> fiscalYearMakerNamesInModules = new HashSet<FiscalYearMakerImpl>();
for ( KfsModuleServiceImpl m : moduleServices.values() ) {
if ( m.getModuleConfiguration() instanceof FinancialSystemModuleConfiguration ) {
for ( FiscalYearMaker fym : ((FinancialSystemModuleConfiguration)m.getModuleConfiguration()).getFiscalYearMakers() ) {
fiscalYearMakerNamesInModules.add( (FiscalYearMakerImpl)ProxyUtils.getTargetIfProxied(fym) );
}
} else {
LOG.warn( "Possible problem. KFS Module service does not contain a KFS module configuration: " + m + "\n Contains: " + m.getModuleConfiguration().getClass().getName() );
}
}
Set<FiscalYearMakerImpl> beansNotReferencedInModules = new HashSet<FiscalYearMakerImpl>( fiscalYearMakerNamesInBeans );
beansNotReferencedInModules.removeAll(fiscalYearMakerNamesInModules);
Set<FiscalYearMakerImpl> moduleFiscalYearMakersWithNoBeans = new HashSet<FiscalYearMakerImpl>( fiscalYearMakerNamesInModules );
moduleFiscalYearMakersWithNoBeans.removeAll(fiscalYearMakerNamesInBeans);
assertEquals( "All of the FiscalYearMaker definitions must be referenced in the module definitions.", Collections.emptySet(), beansNotReferencedInModules );
assertEquals( "All of the FiscalYearMakers in the module definitions must be defined in the Spring context.", Collections.emptySet(), moduleFiscalYearMakersWithNoBeans );
}
public void testParentBeansShouldBeAbstract() {
List<String> failingBeanNames = new ArrayList<String>();
for ( String beanName : SpringContext.applicationContext.getBeanDefinitionNames() ) {
BeanDefinition beanDef = SpringContext.applicationContext.getBeanFactory().getBeanDefinition(beanName);
// skip entries in the rice import files or in testing files
if ( StringUtils.contains( beanDef.getResourceDescription(), "spring-kfs-imported-rice-beans.xml" )
|| StringUtils.contains( beanDef.getResourceDescription(), "-test.xml" ) ) {
continue;
}
if ( beanName.endsWith("-parentBean") && !beanDef.isAbstract() ) {
failingBeanNames.add(beanName + " : " + beanDef.getResourceDescription()+"\n");
}
}
assertEquals( "The following parent beans are not defined as abstract:\n" + failingBeanNames, 0, failingBeanNames.size() );
}
public void testServicesShouldHaveParentBeans() {
List<String> failingBeanNames = new ArrayList<String>();
for ( String beanName : SpringContext.applicationContext.getBeanDefinitionNames() ) {
// skip testing mock beans
if ( StringUtils.containsIgnoreCase(beanName, "mock") ) {
continue;
}
BeanDefinition beanDef = SpringContext.applicationContext.getBeanFactory().getBeanDefinition(beanName);
// skip entries in the rice import files or in testing files
if ( StringUtils.contains( beanDef.getResourceDescription(), "spring-kfs-imported-rice-beans.xml" )
|| StringUtils.contains( beanDef.getResourceDescription(), "-test.xml" ) ) {
continue;
}
String serviceClass = beanDef.getBeanClassName();
if ( StringUtils.contains(serviceClass, "service.impl") && //should be a service
!StringUtils.startsWith(serviceClass, "org.kuali.rice") && //let rice test their code
!beanDef.isAbstract() ) { //abstract = parent
try {
BeanDefinition parentBean = SpringContext.applicationContext.getBeanFactory().getBeanDefinition(beanName + "-parentBean");
String parentClass = parentBean.getBeanClassName();
} catch ( NoSuchBeanDefinitionException ex ) {
failingBeanNames.add(beanName + " : " + beanDef.getResourceDescription()+"\n");
}
}
}
assertEquals( "The following service beans do not have \"-parentBean\"s:\n" + failingBeanNames, 0, failingBeanNames.size() );
}
// DAOs should extend from the xxxx class
public void testDAOsShouldBeDAOs() throws Exception {
List<String> failingBeanNames = new ArrayList<String>();
for ( String beanName : SpringContext.applicationContext.getBeanDefinitionNames() ) {
BeanDefinition beanDef = SpringContext.applicationContext.getBeanFactory().getBeanDefinition(beanName);
// skip entries in the rice import files or in testing files
if ( StringUtils.contains( beanDef.getResourceDescription(), "spring-kfs-imported-rice-beans.xml" )
|| StringUtils.contains( beanDef.getResourceDescription(), "-test.xml" ) ) {
continue;
}
if ( !beanDef.isAbstract() ) {
Object service = TestUtils.getUnproxiedService(beanName);
if ( beanName.endsWith("Dao")
&& !service.getClass().getName().endsWith( "Proxy" )
&& !service.getClass().getName().startsWith( "org.kuali.rice" ) ) {
if ( !(service instanceof PlatformAwareDao) ) {
failingBeanNames.add( " *** FAIL: " + beanName + " does not implement PlatformAwareDao (is " + service.getClass().getName() + ")\n" + " : " + beanDef.getResourceDescription() + "\n" );
}
if ( !(service instanceof PlatformAwareDaoBaseOjb)
&& !(service instanceof PlatformAwareDaoBaseJdbc)) {
failingBeanNames.add( " *** FAIL: " + beanName + " does not extend PlatformAwareDaoBaseOjb/Jdbc (is " + service.getClass().getName() + ")\n" + " : " + beanDef.getResourceDescription() + "\n" );
}
}
}
}
for ( Map.Entry<String,PlatformAwareDao> dao : SpringContext.getBeansOfType(PlatformAwareDao.class).entrySet() ) {
Object service = ProxyUtils.getTargetIfProxied(dao.getValue());
if ( !dao.getKey().endsWith("Dao" )
&& !dao.getKey().endsWith("DaoBase" )
&& !service.getClass().getName().startsWith( "org.kuali.rice" )
&& !FiscalYearMakerImpl.class.isAssignableFrom(service.getClass())) {
failingBeanNames.add( " *** FAIL: Bean " + dao.getKey() + " implements PlatformAwareDao (" + service.getClass().getName() + ") but its name does not end in 'Dao'\n");
}
}
assertEquals( "The following problems were detected in the DAO definitions:\n" + failingBeanNames, 0, failingBeanNames.size() );
}
}