1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.springframework.batch.core.launch.support;
17
18 import java.io.IOException;
19 import java.util.ArrayList;
20 import java.util.Arrays;
21 import java.util.Collections;
22 import java.util.List;
23
24 import org.apache.commons.logging.Log;
25 import org.apache.commons.logging.LogFactory;
26 import org.springframework.batch.core.Job;
27 import org.springframework.batch.core.configuration.DuplicateJobException;
28 import org.springframework.batch.core.configuration.JobFactory;
29 import org.springframework.batch.core.configuration.JobRegistry;
30 import org.springframework.batch.core.configuration.support.DefaultJobLoader;
31 import org.springframework.batch.core.configuration.support.GenericApplicationContextFactory;
32 import org.springframework.batch.core.configuration.support.JobLoader;
33 import org.springframework.batch.core.launch.JobLauncher;
34 import org.springframework.beans.factory.BeanFactory;
35 import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
36 import org.springframework.context.ApplicationContext;
37 import org.springframework.context.support.ClassPathXmlApplicationContext;
38 import org.springframework.core.io.Resource;
39 import org.springframework.util.Assert;
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63 public class JobRegistryBackgroundJobRunner {
64
65
66
67
68
69
70 public static final String EMBEDDED = JobRegistryBackgroundJobRunner.class.getSimpleName() + ".EMBEDDED";
71
72 private static Log logger = LogFactory.getLog(JobRegistryBackgroundJobRunner.class);
73
74 private JobLoader jobLoader;
75
76 private ApplicationContext parentContext = null;
77
78 public static boolean testing = false;
79
80 final private String parentContextPath;
81
82 private JobRegistry jobRegistry;
83
84 private static List<Exception> errors = Collections.synchronizedList(new ArrayList<Exception>());
85
86
87
88
89 public JobRegistryBackgroundJobRunner(String parentContextPath) {
90 super();
91 this.parentContextPath = parentContextPath;
92 }
93
94
95
96
97
98
99 public void setJobLoader(JobLoader jobLoader) {
100 this.jobLoader = jobLoader;
101 }
102
103
104
105
106
107
108 public void setJobRegistry(JobRegistry jobRegistry) {
109 this.jobRegistry = jobRegistry;
110 }
111
112
113
114
115
116
117 public static List<Exception> getErrors() {
118 synchronized (errors) {
119 return new ArrayList<Exception>(errors);
120 }
121 }
122
123 private void register(String[] paths) throws DuplicateJobException, IOException {
124
125 maybeCreateJobLoader();
126
127 for (int i = 0; i < paths.length; i++) {
128
129 Resource[] resources = parentContext.getResources(paths[i]);
130
131 for (int j = 0; j < resources.length; j++) {
132
133 Resource path = resources[j];
134 logger.info("Registering Job definitions from " + Arrays.toString(resources));
135
136 GenericApplicationContextFactory factory = new GenericApplicationContextFactory(path);
137 factory.setApplicationContext(parentContext);
138 jobLoader.load(factory);
139 }
140
141 }
142
143 }
144
145
146
147
148
149 private void maybeCreateJobLoader() {
150
151 if (jobLoader != null) {
152 return;
153 }
154
155 String[] names = parentContext.getBeanNamesForType(JobLoader.class);
156 if (names.length == 0) {
157 if (parentContext.containsBean("jobLoader")) {
158 jobLoader = parentContext.getBean("jobLoader", JobLoader.class);
159 return;
160 }
161 if (jobRegistry != null) {
162 jobLoader = new DefaultJobLoader(jobRegistry);
163 return;
164 }
165 }
166
167 jobLoader = parentContext.getBean(names[0], JobLoader.class);
168 return;
169
170 }
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195 public static void main(String... args) throws Exception {
196
197 Assert.state(args.length >= 1, "At least one argument (the parent context path) must be provided.");
198
199 final JobRegistryBackgroundJobRunner launcher = new JobRegistryBackgroundJobRunner(args[0]);
200 errors.clear();
201
202 logger.info("Starting job registry in parent context from XML at: [" + args[0] + "]");
203
204 new Thread(new Runnable() {
205 @Override
206 public void run() {
207 try {
208 launcher.run();
209 }
210 catch (RuntimeException e) {
211 errors.add(e);
212 throw e;
213 }
214 };
215 }).start();
216
217 logger.info("Waiting for parent context to start.");
218 while (launcher.parentContext == null && errors.isEmpty()) {
219 Thread.sleep(100L);
220 }
221
222 synchronized (errors) {
223 if (!errors.isEmpty()) {
224 logger.info(errors.size() + " errors detected on startup of parent context. Rethrowing.");
225 throw errors.get(0);
226 }
227 }
228 errors.clear();
229
230
231 final String[] paths = new String[args.length - 1];
232 System.arraycopy(args, 1, paths, 0, paths.length);
233
234 logger.info("Parent context started. Registering jobs from paths: " + Arrays.asList(paths));
235 launcher.register(paths);
236
237 if (System.getProperty(EMBEDDED) != null) {
238 launcher.destroy();
239 return;
240 }
241
242 synchronized (JobRegistryBackgroundJobRunner.class) {
243 System.out
244 .println("Started application. Interrupt (CTRL-C) or call JobRegistryBackgroundJobRunner.stop() to exit.");
245 JobRegistryBackgroundJobRunner.class.wait();
246 }
247 launcher.destroy();
248
249 }
250
251
252
253
254
255
256 private void destroy() throws Exception {
257 jobLoader.clear();
258 }
259
260 private void run() {
261 final ApplicationContext parent = new ClassPathXmlApplicationContext(parentContextPath);
262 parent.getAutowireCapableBeanFactory().autowireBeanProperties(this,
263 AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, false);
264 parent.getAutowireCapableBeanFactory().initializeBean(this, getClass().getSimpleName());
265 this.parentContext = parent;
266 }
267
268
269
270
271 public static void stop() {
272 synchronized (JobRegistryBackgroundJobRunner.class) {
273 JobRegistryBackgroundJobRunner.class.notify();
274 }
275 }
276
277 }