View Javadoc

1   /*
2    * Copyright 2006-2007 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.springframework.batch.core.configuration.support;
18  
19  import java.util.ArrayList;
20  import java.util.Collection;
21  
22  import org.springframework.batch.core.Job;
23  import org.springframework.batch.core.configuration.DuplicateJobException;
24  import org.springframework.batch.core.configuration.JobRegistry;
25  import org.springframework.beans.factory.InitializingBean;
26  import org.springframework.context.ApplicationContext;
27  import org.springframework.context.ApplicationContextAware;
28  import org.springframework.context.ApplicationEvent;
29  import org.springframework.context.ApplicationListener;
30  import org.springframework.context.Lifecycle;
31  import org.springframework.context.event.ContextClosedEvent;
32  import org.springframework.context.event.ContextRefreshedEvent;
33  import org.springframework.core.Ordered;
34  import org.springframework.util.Assert;
35  
36  /**
37   * Loads and unloads {@link Job Jobs} when the application context is created and destroyed. Each resource provided is
38   * loaded as an application context with the current context as its parent, and then all the jobs from the child context
39   * are registered under their bean names. A {@link JobRegistry} is required.
40   *
41   * @author Lucas Ward
42   * @author Dave Syer
43   *
44   * @since 2.1
45   */
46  public class AutomaticJobRegistrar implements Ordered, Lifecycle, ApplicationListener, ApplicationContextAware,
47  		InitializingBean {
48  
49  	private Collection<ApplicationContextFactory> applicationContextFactories = new ArrayList<ApplicationContextFactory>();
50  
51  	private JobLoader jobLoader;
52  
53  	private ApplicationContext applicationContext;
54  
55  	private volatile boolean running = false;
56  
57  	private Object lifecycleMonitor = new Object();
58  
59  	private int order = Ordered.LOWEST_PRECEDENCE;
60  
61  	/**
62  	 * The enclosing application context, which can be used to check if {@link ApplicationEvent events} come from the
63  	 * expected source.
64  	 *
65  	 * @param applicationContext the enclosing application context if there is one
66  	 * @see ApplicationContextAware#setApplicationContext(ApplicationContext)
67  	 */
68  	@Override
69  	public void setApplicationContext(ApplicationContext applicationContext) {
70  		this.applicationContext = applicationContext;
71  	}
72  
73  	/**
74  	 * Add some factories to the set that will be used to load contexts and jobs.
75  	 *
76  	 * @param applicationContextFactory the {@link ApplicationContextFactory} values to use
77  	 */
78  	public void addApplicationContextFactory(ApplicationContextFactory applicationContextFactory) {
79  		if (applicationContextFactory instanceof ApplicationContextAware) {
80  			((ApplicationContextAware) applicationContextFactory).setApplicationContext(applicationContext);
81  		}
82  		this.applicationContextFactories.add(applicationContextFactory);
83  	}
84  
85  	/**
86  	 * Add some factories to the set that will be used to load contexts and jobs.
87  	 *
88  	 * @param applicationContextFactories the {@link ApplicationContextFactory} values to use
89  	 */
90  	public void setApplicationContextFactories(ApplicationContextFactory[] applicationContextFactories) {
91  		for (ApplicationContextFactory applicationContextFactory : applicationContextFactories) {
92  			this.applicationContextFactories.add(applicationContextFactory);
93  		}
94  	}
95  
96  	/**
97  	 * The job loader that will be used to load and manage jobs.
98  	 *
99  	 * @param jobLoader the {@link JobLoader} to set
100 	 */
101 	public void setJobLoader(JobLoader jobLoader) {
102 		this.jobLoader = jobLoader;
103 	}
104 
105 	@Override
106 	public int getOrder() {
107 		return order;
108 	}
109 
110 	/**
111 	 * The order to start up and shutdown.
112 	 * @param order the order (default {@link Ordered#LOWEST_PRECEDENCE}).
113 	 * @see Ordered
114 	 */
115 	public void setOrder(int order) {
116 		this.order = order;
117 	}
118 
119 	/**
120 	 */
121 	@Override
122 	public void afterPropertiesSet() {
123 
124 		Assert.state(jobLoader != null, "A JobLoader must be provided");
125 
126 	}
127 
128 	/**
129 	 * Creates all the application contexts required and set up job registry entries with all the instances of
130 	 * {@link Job} found therein. Also closes the contexts when the enclosing context is closed.
131 	 *
132 	 * @see InitializingBean#afterPropertiesSet()
133 	 */
134 	@Override
135 	public final void onApplicationEvent(ApplicationEvent event) {
136 		// TODO: With Spring 3 a SmartLifecycle is started automatically
137 		if (event.getSource() == applicationContext) {
138 			if (event instanceof ContextRefreshedEvent) {
139 				start();
140 			}
141 			else if (event instanceof ContextClosedEvent) {
142 				stop();
143 			}
144 		}
145 	}
146 
147 	/**
148 	 * Delegates to {@link JobLoader#clear()}.
149 	 *
150 	 * @see Lifecycle#stop()
151 	 */
152 	@Override
153 	public void stop() {
154 		synchronized (this.lifecycleMonitor) {
155 			jobLoader.clear();
156 			running = false;
157 		}
158 	}
159 
160 	/**
161 	 * Take all the contexts from the factories provided and pass them to the {@link JobLoader}.
162 	 *
163 	 * @see Lifecycle#start()
164 	 */
165 	@Override
166 	public void start() {
167 		synchronized (this.lifecycleMonitor) {
168 			if (running) {
169 				return;
170 			}
171 			for (ApplicationContextFactory factory : applicationContextFactories) {
172 				try {
173 					jobLoader.load(factory);
174 				}
175 				catch (DuplicateJobException e) {
176 					throw new IllegalStateException(e);
177 				}
178 			}
179 			running = true;
180 		}
181 	}
182 
183 	/**
184 	 * Check if this component has been started.
185 	 *
186 	 * @return true if started successfully and not stopped
187 	 * @see Lifecycle#isRunning()
188 	 */
189 	@Override
190 	public boolean isRunning() {
191 		synchronized (this.lifecycleMonitor) {
192 			return running;
193 		}
194 	}
195 
196 }