001/*
002 * Copyright 2006-2018 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 */
016
017package org.springframework.batch.core.configuration.support;
018
019import java.util.ArrayList;
020import java.util.Collection;
021
022import org.springframework.batch.core.Job;
023import org.springframework.batch.core.configuration.DuplicateJobException;
024import org.springframework.batch.core.configuration.JobRegistry;
025import org.springframework.beans.factory.InitializingBean;
026import org.springframework.context.ApplicationContext;
027import org.springframework.context.ApplicationContextAware;
028import org.springframework.context.Lifecycle;
029import org.springframework.context.SmartLifecycle;
030import org.springframework.context.event.ApplicationContextEvent;
031import org.springframework.core.Ordered;
032import org.springframework.util.Assert;
033
034/**
035 * Loads and unloads {@link Job Jobs} when the application context is created and destroyed. Each resource provided is
036 * loaded as an application context with the current context as its parent, and then all the jobs from the child context
037 * are registered under their bean names. A {@link JobRegistry} is required.
038 *
039 * @author Lucas Ward
040 * @author Dave Syer
041 * @author Mahmoud Ben Hassine
042 *
043 * @since 2.1
044 */
045public class AutomaticJobRegistrar implements Ordered, SmartLifecycle, ApplicationContextAware,
046                InitializingBean {
047
048        private Collection<ApplicationContextFactory> applicationContextFactories = new ArrayList<ApplicationContextFactory>();
049
050        private JobLoader jobLoader;
051
052        private ApplicationContext applicationContext;
053
054        private volatile boolean running = false;
055
056        private int phase = Integer.MIN_VALUE + 1000;
057
058        private boolean autoStartup = true;
059
060        private Object lifecycleMonitor = new Object();
061
062        private int order = Ordered.LOWEST_PRECEDENCE;
063
064        /**
065         * The enclosing application context, which can be used to check if {@link ApplicationContextEvent events} come
066         * from the expected source.
067         *
068         * @param applicationContext the enclosing application context if there is one
069         * @see ApplicationContextAware#setApplicationContext(ApplicationContext)
070         */
071        @Override
072        public void setApplicationContext(ApplicationContext applicationContext) {
073                this.applicationContext = applicationContext;
074        }
075
076        /**
077         * Add some factories to the set that will be used to load contexts and jobs.
078         *
079         * @param applicationContextFactory the {@link ApplicationContextFactory} values to use
080         */
081        public void addApplicationContextFactory(ApplicationContextFactory applicationContextFactory) {
082                if (applicationContextFactory instanceof ApplicationContextAware) {
083                        ((ApplicationContextAware) applicationContextFactory).setApplicationContext(applicationContext);
084                }
085                this.applicationContextFactories.add(applicationContextFactory);
086        }
087
088        /**
089         * Add some factories to the set that will be used to load contexts and jobs.
090         *
091         * @param applicationContextFactories the {@link ApplicationContextFactory} values to use
092         */
093        public void setApplicationContextFactories(ApplicationContextFactory[] applicationContextFactories) {
094                for (ApplicationContextFactory applicationContextFactory : applicationContextFactories) {
095                        this.applicationContextFactories.add(applicationContextFactory);
096                }
097        }
098
099        /**
100         * The job loader that will be used to load and manage jobs.
101         *
102         * @param jobLoader the {@link JobLoader} to set
103         */
104        public void setJobLoader(JobLoader jobLoader) {
105                this.jobLoader = jobLoader;
106        }
107
108        @Override
109        public int getOrder() {
110                return order;
111        }
112
113        /**
114         * The order to start up and shutdown.
115         * @param order the order (default {@link Ordered#LOWEST_PRECEDENCE}).
116         * @see Ordered
117         */
118        public void setOrder(int order) {
119                this.order = order;
120        }
121
122        /**
123         */
124        @Override
125        public void afterPropertiesSet() {
126
127                Assert.state(jobLoader != null, "A JobLoader must be provided");
128
129        }
130
131        /**
132         * Delegates to {@link JobLoader#clear()}.
133         *
134         * @see Lifecycle#stop()
135         */
136        @Override
137        public void stop() {
138                synchronized (this.lifecycleMonitor) {
139                        jobLoader.clear();
140                        running = false;
141                }
142        }
143
144        /**
145         * Take all the contexts from the factories provided and pass them to the {@link JobLoader}.
146         *
147         * @see Lifecycle#start()
148         */
149        @Override
150        public void start() {
151                synchronized (this.lifecycleMonitor) {
152                        if (running) {
153                                return;
154                        }
155                        for (ApplicationContextFactory factory : applicationContextFactories) {
156                                try {
157                                        jobLoader.load(factory);
158                                }
159                                catch (DuplicateJobException e) {
160                                        throw new IllegalStateException(e);
161                                }
162                        }
163                        running = true;
164                }
165        }
166
167        /**
168         * Check if this component has been started.
169         *
170         * @return true if started successfully and not stopped
171         * @see Lifecycle#isRunning()
172         */
173        @Override
174        public boolean isRunning() {
175                synchronized (this.lifecycleMonitor) {
176                        return running;
177                }
178        }
179
180        @Override
181        public boolean isAutoStartup() {
182                return autoStartup;
183        }
184
185        /**
186         * @param autoStartup true for auto start.
187         * @see #isAutoStartup()
188         */
189        public void setAutoStartup(boolean autoStartup) {
190                this.autoStartup = autoStartup;
191        }
192
193        @Override
194        public int getPhase() {
195                return phase;
196        }
197
198        /**
199         * @param phase the phase.
200         * @see #getPhase()
201         */
202        public void setPhase(int phase) {
203                this.phase = phase;
204        }
205
206        @Override
207        public void stop(Runnable callback) {
208                stop();
209                callback.run();
210        }
211
212}