001/* 002 * Copyright 2006-2013 the original author or authors. 003 * 004 * Licensed under the Apache 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 * https://www.apache.org/licenses/LICENSE-2.0 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 */ 016package org.springframework.batch.core.configuration.xml; 017 018import org.springframework.batch.core.repository.JobRepository; 019import org.springframework.batch.core.step.AbstractStep; 020import org.springframework.beans.BeansException; 021import org.springframework.beans.MutablePropertyValues; 022import org.springframework.beans.PropertyValue; 023import org.springframework.beans.factory.config.BeanDefinition; 024import org.springframework.beans.factory.config.BeanFactoryPostProcessor; 025import org.springframework.beans.factory.config.BeanPostProcessor; 026import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; 027import org.springframework.beans.factory.config.RuntimeBeanReference; 028import org.springframework.beans.factory.support.AbstractBeanDefinition; 029import org.springframework.context.ApplicationContext; 030import org.springframework.context.ApplicationContextAware; 031import org.springframework.transaction.PlatformTransactionManager; 032 033/** 034 * Post-process jobs and steps defined using the batch namespace to inject 035 * dependencies. 036 * 037 * @author Dan Garrette 038 * @since 2.0.1 039 */ 040public class CoreNamespacePostProcessor implements BeanPostProcessor, BeanFactoryPostProcessor, ApplicationContextAware { 041 042 private static final String DEFAULT_JOB_REPOSITORY_NAME = "jobRepository"; 043 044 private static final String DEFAULT_TRANSACTION_MANAGER_NAME = "transactionManager"; 045 046 private static final String JOB_FACTORY_PROPERTY_NAME = "jobParserJobFactoryBeanRef"; 047 048 private static final String JOB_REPOSITORY_PROPERTY_NAME = "jobRepository"; 049 050 private ApplicationContext applicationContext; 051 052 @Override 053 public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { 054 for (String beanName : beanFactory.getBeanDefinitionNames()) { 055 injectJobRepositoryIntoSteps(beanName, beanFactory); 056 overrideStepClass(beanName, beanFactory); 057 } 058 } 059 060 /** 061 * Automatically inject job-repository from a job into its steps. Only 062 * inject if the step is an AbstractStep or StepParserStepFactoryBean. 063 * 064 * @param beanName 065 * @param beanFactory 066 */ 067 private void injectJobRepositoryIntoSteps(String beanName, ConfigurableListableBeanFactory beanFactory) { 068 BeanDefinition bd = beanFactory.getBeanDefinition(beanName); 069 if (bd.hasAttribute(JOB_FACTORY_PROPERTY_NAME)) { 070 MutablePropertyValues pvs = bd.getPropertyValues(); 071 if (beanFactory.isTypeMatch(beanName, AbstractStep.class)) { 072 String jobName = (String) bd.getAttribute(JOB_FACTORY_PROPERTY_NAME); 073 PropertyValue jobRepository = BeanDefinitionUtils.getPropertyValue(jobName, 074 JOB_REPOSITORY_PROPERTY_NAME, beanFactory); 075 if (jobRepository != null) { 076 // Set the job's JobRepository onto the step 077 pvs.addPropertyValue(jobRepository); 078 } 079 else { 080 // No JobRepository found, so inject the default 081 RuntimeBeanReference jobRepositoryBeanRef = new RuntimeBeanReference(DEFAULT_JOB_REPOSITORY_NAME); 082 pvs.addPropertyValue(JOB_REPOSITORY_PROPERTY_NAME, jobRepositoryBeanRef); 083 } 084 } 085 } 086 } 087 088 /** 089 * If any of the beans in the parent hierarchy is a <step/> with a 090 * <tasklet/>, then the bean class must be 091 * {@link StepParserStepFactoryBean}. 092 * 093 * @param beanName 094 * @param beanFactory 095 */ 096 private void overrideStepClass(String beanName, ConfigurableListableBeanFactory beanFactory) { 097 BeanDefinition bd = beanFactory.getBeanDefinition(beanName); 098 Object isNamespaceStep = BeanDefinitionUtils 099 .getAttribute(beanName, "isNamespaceStep", beanFactory); 100 if (isNamespaceStep != null && (Boolean) isNamespaceStep == true) { 101 ((AbstractBeanDefinition) bd).setBeanClass(StepParserStepFactoryBean.class); 102 } 103 } 104 105 @Override 106 public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { 107 return injectDefaults(bean); 108 } 109 110 /** 111 * Inject defaults into factory beans. 112 * <ul> 113 * <li>Inject "jobRepository" into any {@link JobParserJobFactoryBean} 114 * without a jobRepository. 115 * <li>Inject "transactionManager" into any 116 * {@link StepParserStepFactoryBean} without a transactionManager. 117 * </ul> 118 * 119 * @param bean 120 * @return 121 */ 122 private Object injectDefaults(Object bean) { 123 if (bean instanceof JobParserJobFactoryBean) { 124 JobParserJobFactoryBean fb = (JobParserJobFactoryBean) bean; 125 JobRepository jobRepository = fb.getJobRepository(); 126 if (jobRepository == null) { 127 fb.setJobRepository((JobRepository) applicationContext.getBean(DEFAULT_JOB_REPOSITORY_NAME)); 128 } 129 } else if (bean instanceof StepParserStepFactoryBean) { 130 StepParserStepFactoryBean<?, ?> fb = (StepParserStepFactoryBean<?, ?>) bean; 131 JobRepository jobRepository = fb.getJobRepository(); 132 if (jobRepository == null) { 133 fb.setJobRepository((JobRepository) applicationContext.getBean(DEFAULT_JOB_REPOSITORY_NAME)); 134 } 135 PlatformTransactionManager transactionManager = fb.getTransactionManager(); 136 if (transactionManager == null && fb.requiresTransactionManager()) { 137 fb.setTransactionManager((PlatformTransactionManager) applicationContext 138 .getBean(DEFAULT_TRANSACTION_MANAGER_NAME)); 139 } 140 } 141 return bean; 142 } 143 144 @Override 145 public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { 146 return bean; 147 } 148 149 @Override 150 public void setApplicationContext(ApplicationContext applicationContext) { 151 this.applicationContext = applicationContext; 152 } 153}