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.support;
017
018import java.util.Collection;
019import java.util.HashSet;
020
021import org.apache.commons.logging.Log;
022import org.apache.commons.logging.LogFactory;
023import org.springframework.batch.core.Job;
024import org.springframework.batch.core.configuration.DuplicateJobException;
025import org.springframework.batch.core.configuration.JobLocator;
026import org.springframework.batch.core.configuration.JobRegistry;
027import org.springframework.beans.BeansException;
028import org.springframework.beans.FatalBeanException;
029import org.springframework.beans.factory.BeanFactory;
030import org.springframework.beans.factory.BeanFactoryAware;
031import org.springframework.beans.factory.DisposableBean;
032import org.springframework.beans.factory.InitializingBean;
033import org.springframework.beans.factory.config.BeanDefinition;
034import org.springframework.beans.factory.config.BeanPostProcessor;
035import org.springframework.beans.factory.support.DefaultListableBeanFactory;
036import org.springframework.util.Assert;
037
038/**
039 * A {@link BeanPostProcessor} that registers {@link Job} beans with a
040 * {@link JobRegistry}. Include a bean of this type along with your job
041 * configuration, and use the same {@link JobRegistry} as a {@link JobLocator}
042 * when you need to locate a {@link Job} to launch.
043 *
044 * @author Dave Syer
045 *
046 */
047public class JobRegistryBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware, InitializingBean,
048DisposableBean {
049
050        private static Log logger = LogFactory.getLog(JobRegistryBeanPostProcessor.class);
051
052        // It doesn't make sense for this to have a default value...
053        private JobRegistry jobRegistry = null;
054
055        private Collection<String> jobNames = new HashSet<String>();
056
057        private String groupName = null;
058
059        private DefaultListableBeanFactory beanFactory;
060
061        /**
062         * The group name for jobs registered by this component. Optional (defaults
063         * to null, which means that jobs are registered with their bean names).
064         * Useful where there is a hierarchy of application contexts all
065         * contributing to the same {@link JobRegistry}: child contexts can then
066         * define an instance with a unique group name to avoid clashes between job
067         * names.
068         *
069         * @param groupName the groupName to set
070         */
071        public void setGroupName(String groupName) {
072                this.groupName = groupName;
073        }
074
075        /**
076         * Injection setter for {@link JobRegistry}.
077         *
078         * @param jobRegistry the jobConfigurationRegistry to set
079         */
080        public void setJobRegistry(JobRegistry jobRegistry) {
081                this.jobRegistry = jobRegistry;
082        }
083
084        /*
085         * (non-Javadoc)
086         *
087         * @see
088         * org.springframework.beans.factory.BeanFactoryAware#setBeanFactory(org
089         * .springframework.beans.factory.BeanFactory)
090         */
091        @Override
092        public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
093                if (beanFactory instanceof DefaultListableBeanFactory) {
094                        this.beanFactory = (DefaultListableBeanFactory) beanFactory;
095                }
096        }
097
098        /**
099         * Make sure the registry is set before use.
100         *
101         * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
102         */
103        @Override
104        public void afterPropertiesSet() throws Exception {
105                Assert.notNull(jobRegistry, "JobRegistry must not be null");
106        }
107
108        /**
109         * Unregister all the {@link Job} instances that were registered by this
110         * post processor.
111         * @see org.springframework.beans.factory.DisposableBean#destroy()
112         */
113        @Override
114        public void destroy() throws Exception {
115                for (String name : jobNames) {
116                        if (logger.isDebugEnabled()) {
117                                logger.debug("Unregistering job: " + name);
118                        }
119                        jobRegistry.unregister(name);
120                }
121                jobNames.clear();
122        }
123
124        /**
125         * If the bean is an instance of {@link Job} then register it.
126         * @throws FatalBeanException if there is a {@link DuplicateJobException}.
127         *
128         * @see org.springframework.beans.factory.config.BeanPostProcessor#postProcessAfterInitialization(java.lang.Object,
129         * java.lang.String)
130         */
131        @Override
132        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
133                if (bean instanceof Job) {
134                        Job job = (Job) bean;
135                        try {
136                                String groupName = this.groupName;
137                                if (beanFactory != null && beanFactory.containsBean(beanName)) {
138                                        groupName = getGroupName(beanFactory.getBeanDefinition(beanName), job);
139                                }
140                                job = groupName==null ? job : new GroupAwareJob(groupName, job);
141                                ReferenceJobFactory jobFactory = new ReferenceJobFactory(job);
142                                String name = jobFactory.getJobName();
143                                if (logger.isDebugEnabled()) {
144                                        logger.debug("Registering job: " + name);
145                                }
146                                jobRegistry.register(jobFactory);
147                                jobNames.add(name);
148                        }
149                        catch (DuplicateJobException e) {
150                                throw new FatalBeanException("Cannot register job configuration", e);
151                        }
152                        return job;
153                }
154                return bean;
155        }
156
157        /**
158         * Determine a group name for the job to be registered. Default
159         * implementation just returns the {@link #setGroupName(String) groupName}
160         * configured. Provides an extension point for specialised subclasses.
161         *
162         * @param beanDefinition the bean definition for the job
163         * @param job the job
164         * @return a group name for the job (or null if not needed)
165         */
166        protected String getGroupName(BeanDefinition beanDefinition, Job job) {
167                return groupName;
168        }
169
170        /**
171         * Do nothing.
172         *
173         * @see org.springframework.beans.factory.config.BeanPostProcessor#postProcessBeforeInitialization(java.lang.Object,
174         * java.lang.String)
175         */
176        @Override
177        public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
178                return bean;
179        }
180}