/* * Copyright 2013-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.xd.dirt.plugins.job; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.batch.core.Job; import org.springframework.batch.core.JobExecutionListener; import org.springframework.batch.core.StepListener; import org.springframework.batch.core.configuration.JobRegistry; import org.springframework.batch.core.configuration.support.JobRegistryBeanPostProcessor; import org.springframework.batch.core.configuration.xml.JobParserJobFactoryBean; import org.springframework.batch.core.configuration.xml.StepParserStepFactoryBean; import org.springframework.batch.core.job.AbstractJob; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.util.Assert; import org.springframework.xd.dirt.plugins.job.support.listener.XDJobListenerConstants; /** * JobRegistryBeanPostProcessor that processes batch job from the job module. * * @author Ilayaperumal Gopinathan * @author Michael Minella */ public class BatchJobRegistryBeanPostProcessor extends JobRegistryBeanPostProcessor implements BeanFactoryAware, XDJobListenerConstants { private static final Logger logger = LoggerFactory.getLogger(BatchJobRegistryBeanPostProcessor.class); public static final String JOB = "job"; private JobRegistry jobRegistry; private DefaultListableBeanFactory beanFactory; private DistributedJobLocator jobLocator; private String groupName; private List<JobExecutionListener> jobExecutionListeners = new ArrayList<JobExecutionListener>(); private List<StepListener> stepListeners = new ArrayList<StepListener>(); @Override public void setJobRegistry(JobRegistry jobRegistry) { this.jobRegistry = jobRegistry; super.setJobRegistry(jobRegistry); } @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { if (beanFactory instanceof DefaultListableBeanFactory) { this.beanFactory = (DefaultListableBeanFactory) beanFactory; } super.setBeanFactory(beanFactory); } public void setJobLocator(DistributedJobLocator jobLocator) { this.jobLocator = jobLocator; } @Override public void setGroupName(String groupName) { this.groupName = groupName; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) { if (bean instanceof StepParserStepFactoryBean<?, ?>) { addStepListeners(); if (!stepListeners.isEmpty()) { // Add the step listeners to the step parser factory bean ((StepParserStepFactoryBean) bean).setListeners(this.stepListeners.toArray(new StepListener[this .stepListeners.size()])); } } else if (bean instanceof Job) { if (!jobRegistry.getJobNames().contains(groupName)) { String[] beansOfType = beanFactory.getBeanNamesForType(Job.class); if (beansOfType.length > 1) { if (beanName.equalsIgnoreCase(JOB)) { postProcessJob(bean, beanName); } else { logger.debug(beanName + " was not registered as a job since the context has more than one job " + "defined and it's id is not 'job'"); } } else { postProcessJob(bean, beanName); } } else if (jobRegistry.getJobNames().contains(groupName)) { throw new BatchJobAlreadyExistsInRegistryException(groupName); } } return bean; } private void postProcessJob(Object bean, String beanName) { AbstractJob job = (AbstractJob) bean; job.setName(this.groupName); addJobExecutionListener(); if (!this.jobExecutionListeners.isEmpty()) { // Add the job execution listeners to the job parser factory bean job.setJobExecutionListeners(this.jobExecutionListeners.toArray(new JobExecutionListener[this.jobExecutionListeners.size()])); } // Add the job name, job parameters incrementer and job restartable flag to {@link DistributedJobLocator} // Since, the Spring batch doesn't have persistent JobRegistry, the {@link DistributedJobLocator} // acts as the store to have jobName , job parameter incrementer and restartable flag to be // used by {@link DistributedJobService} jobLocator.addJob(this.groupName, (job.getJobParametersIncrementer() != null) ? true : false, job.isRestartable()); jobLocator.addStepNames(this.groupName, job.getStepNames()); super.postProcessAfterInitialization(bean, beanName); } private void addJobExecutionListener() { // Add all job execution listeners available in the bean factory // We won't have multiple batch job definitions on a given job module; hence all the job execution listeners // available in the bean factory correspond to the job module's batch job. Map<String, JobExecutionListener> listeners = this.beanFactory.getBeansOfType(JobExecutionListener.class); this.jobExecutionListeners.addAll(listeners.values()); } private void addStepListeners() { if (this.beanFactory.containsBean(XD_STEP_EXECUTION_LISTENER_BEAN)) { this.stepListeners.add((StepListener) this.beanFactory.getBean(XD_STEP_EXECUTION_LISTENER_BEAN)); } if (this.beanFactory.containsBean(XD_CHUNK_LISTENER_BEAN)) { this.stepListeners.add((StepListener) this.beanFactory.getBean(XD_CHUNK_LISTENER_BEAN)); } if (this.beanFactory.containsBean(XD_ITEM_LISTENER_BEAN)) { this.stepListeners.add((StepListener) this.beanFactory.getBean(XD_ITEM_LISTENER_BEAN)); } if (this.beanFactory.containsBean(XD_SKIP_LISTENER_BEAN)) { this.stepListeners.add((StepListener) this.beanFactory.getBean(XD_SKIP_LISTENER_BEAN)); } } @Override public void destroy() throws Exception { Assert.notNull(groupName, "JobName should not be null"); jobLocator.deleteJobRegistry(groupName); super.destroy(); } }