EMMA Coverage Report (generated Fri Aug 21 15:59:46 BST 2009)
[all classes][org.springframework.batch.core.launch.support]

COVERAGE SUMMARY FOR SOURCE FILE [JobRegistryBackgroundJobRunner.java]

nameclass, %method, %block, %line, %
JobRegistryBackgroundJobRunner.java100% (2/2)92%  (11/12)84%  (275/326)84%  (58.2/69)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class JobRegistryBackgroundJobRunner$1100% (1/1)100% (2/2)61%  (11/18)57%  (4/7)
run (): void 100% (1/1)42%  (5/12)40%  (2/5)
JobRegistryBackgroundJobRunner$1 (JobRegistryBackgroundJobRunner): void 100% (1/1)100% (6/6)100% (2/2)
     
class JobRegistryBackgroundJobRunner100% (1/1)90%  (9/10)86%  (264/308)88%  (55.2/63)
access$1 (): List 0%   (0/1)0%   (0/2)0%   (0/1)
main (String []): void 100% (1/1)72%  (92/128)71%  (17.7/25)
stop (): void 100% (1/1)77%  (10/13)92%  (2.8/3)
getErrors (): List 100% (1/1)79%  (11/14)89%  (1.8/2)
<static initializer> 100% (1/1)100% (21/21)100% (5/5)
JobRegistryBackgroundJobRunner (String): void 100% (1/1)100% (9/9)100% (4/4)
access$0 (JobRegistryBackgroundJobRunner): void 100% (1/1)100% (3/3)100% (1/1)
register (String []): void 100% (1/1)100% (90/90)100% (15/15)
run (): void 100% (1/1)100% (24/24)100% (6/6)
setRegistry (JobRegistry): void 100% (1/1)100% (4/4)100% (2/2)

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 */
16package org.springframework.batch.core.launch.support;
17 
18import java.io.IOException;
19import java.util.ArrayList;
20import java.util.Arrays;
21import java.util.Collections;
22import java.util.List;
23 
24import org.apache.commons.logging.Log;
25import org.apache.commons.logging.LogFactory;
26import org.springframework.batch.core.Job;
27import org.springframework.batch.core.configuration.DuplicateJobException;
28import org.springframework.batch.core.configuration.JobFactory;
29import org.springframework.batch.core.configuration.JobRegistry;
30import org.springframework.batch.core.configuration.support.ApplicationContextJobFactory;
31import org.springframework.batch.core.configuration.support.ClassPathXmlApplicationContextFactory;
32import org.springframework.batch.core.launch.JobLauncher;
33import org.springframework.beans.factory.BeanFactory;
34import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
35import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
36import org.springframework.beans.factory.xml.XmlBeanFactory;
37import org.springframework.context.ApplicationContext;
38import org.springframework.context.support.ClassPathXmlApplicationContext;
39import org.springframework.core.io.Resource;
40import org.springframework.util.Assert;
41 
42/**
43 * <p>
44 * Command line launcher for registering jobs with a {@link JobRegistry}.
45 * Normally this will be used in conjunction with an external trigger for the
46 * jobs registered, e.g. a JMX MBean wrapper for a {@link JobLauncher}, or a
47 * Quartz trigger.
48 * </p>
49 * 
50 * <p>
51 * With any launch of a batch job within Spring Batch, a Spring context
52 * containing the {@link Job} has to be created. Using this launcher, the jobs
53 * are all registered with a {@link JobRegistry} defined in a parent application
54 * context. The jobs are then set up in child contexts. All dependencies of the
55 * runner will then be satisfied by autowiring by type from the parent
56 * application context. Default values are provided for all fields except the
57 * {@link JobRegistry}. Therefore, if autowiring fails to set it then an
58 * exception will be thrown.
59 * </p>
60 * 
61 * @author Dave Syer
62 * 
63 */
64public class JobRegistryBackgroundJobRunner {
65 
66        /**
67         * System property key that switches the runner to "embedded" mode
68         * (returning immediately from the main method). Useful for testing
69         * purposes.
70         */
71        public static final String EMBEDDED = JobRegistryBackgroundJobRunner.class.getSimpleName() + ".EMBEDDED";
72 
73        private static Log logger = LogFactory.getLog(JobRegistryBackgroundJobRunner.class);
74 
75        private JobRegistry registry;
76 
77        private ApplicationContext parentContext = null;
78 
79        public static boolean testing = false;
80 
81        final private String parentContextPath;
82 
83        private static List<Exception> errors = Collections.synchronizedList(new ArrayList<Exception>());
84 
85        /**
86         * @param parentContextPath
87         */
88        public JobRegistryBackgroundJobRunner(String parentContextPath) {
89                super();
90                this.parentContextPath = parentContextPath;
91        }
92 
93        /**
94         * Public setter for the {@link JobRegistry}.
95         * @param registry the registry to set
96         */
97        public void setRegistry(JobRegistry registry) {
98                this.registry = registry;
99        }
100 
101        /**
102         * Public getter for the startup errors encountered during parent context
103         * creation.
104         * @return the errors
105         */
106        public static List<Exception> getErrors() {
107                synchronized (errors) {
108                        return new ArrayList<Exception>(errors);
109                }
110        }
111 
112        private void register(String[] paths) throws DuplicateJobException, IOException {
113 
114                for (int i = 0; i < paths.length; i++) {
115 
116                        Resource[] resources = parentContext.getResources(paths[i]);
117 
118                        for (int j = 0; j < resources.length; j++) {
119 
120                                Resource path = resources[j];
121                                logger.info("Registering Job definitions from " + Arrays.toString(resources));
122 
123                                ConfigurableListableBeanFactory beanFactory = new XmlBeanFactory(path, parentContext
124                                                .getAutowireCapableBeanFactory());
125                                String[] names = beanFactory.getBeanNamesForType(Job.class);
126 
127                                for (int k = 0; k < names.length; k++) {
128                                        ClassPathXmlApplicationContextFactory factory = new ClassPathXmlApplicationContextFactory();
129                                        factory.setApplicationContext(parentContext);
130                                        factory.setPath(path);
131                                        logger.info("Registering Job definition: " + names[k]);
132                                        registry.register(new ApplicationContextJobFactory(factory, names[k]));
133                                }
134 
135                        }
136 
137                }
138 
139        }
140 
141        /**
142         * Supply a list of application context locations, starting with the parent
143         * context, and followed by the children. The parent must contain a
144         * {@link JobRegistry} and the child contexts are expected to contain
145         * {@link Job} definitions, each of which will be registered wit the
146         * registry.
147         * 
148         * Example usage:
149         * 
150         * <pre>
151         * $ java -classpath ... JobRegistryBackgroundJobRunner job-registry-context.xml job1.xml job2.xml ...
152         * </pre>
153         * 
154         * The child contexts are created only when needed though the
155         * {@link JobFactory} interface (but the XML is validated on startup by
156         * using it to create a {@link BeanFactory} which is then discarded).
157         * 
158         * The parent context is created in a separate thread, and the program will
159         * pause for input in an infinite loop until the user hits any key.
160         * 
161         * @param args the context locations to use (first one is for parent)
162         * @throws Exception if anything goes wrong with the context creation
163         */
164        public static void main(String... args) throws Exception {
165 
166                Assert.state(args.length >= 1, "At least one argument (the parent context path) must be provided.");
167 
168                final JobRegistryBackgroundJobRunner launcher = new JobRegistryBackgroundJobRunner(args[0]);
169                errors.clear();
170 
171                logger.info("Starting job registry in parent context from XML at: [" + args[0] + "]");
172 
173                new Thread(new Runnable() {
174                        public void run() {
175                                try {
176                                        launcher.run();
177                                }
178                                catch (RuntimeException e) {
179                                        errors.add(e);
180                                        throw e;
181                                }
182                        };
183                }).start();
184 
185                logger.info("Waiting for parent context to start.");
186                while (launcher.parentContext == null && errors.isEmpty()) {
187                        Thread.sleep(100L);
188                }
189 
190                synchronized (errors) {
191                        if (!errors.isEmpty()) {
192                                logger.info(errors.size() + " errors detected on startup of parent context.  Rethrowing.");
193                                throw errors.get(0);
194                        }
195                }
196                errors.clear();
197                
198                // Paths to individual job configurations.
199                final String[] paths = new String[args.length - 1];
200                System.arraycopy(args, 1, paths, 0, paths.length);
201 
202                logger.info("Parent context started.  Registering jobs from paths: " + Arrays.asList(paths));
203                launcher.register(paths);
204 
205                if (System.getProperty(EMBEDDED) != null) {
206                        return;
207                }
208 
209                synchronized (JobRegistryBackgroundJobRunner.class) {
210                        System.out
211                                        .println("Started application.  Interrupt (CTRL-C) or call JobRegistryBackgroundJobRunner.stop() to exit.");
212                        JobRegistryBackgroundJobRunner.class.wait();
213                }
214 
215        }
216 
217        private void run() {
218                final ApplicationContext parent = new ClassPathXmlApplicationContext(parentContextPath);
219                parent.getAutowireCapableBeanFactory().autowireBeanProperties(this,
220                                AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, false);
221                parent.getAutowireCapableBeanFactory().initializeBean(this, getClass().getSimpleName());
222                this.parentContext = parent;
223        }
224 
225        /**
226         * If embedded in a JVM, call this method to terminate the main method.
227         */
228        public static void stop() {
229                synchronized (JobRegistryBackgroundJobRunner.class) {
230                        JobRegistryBackgroundJobRunner.class.notify();
231                }
232        }
233 
234}

[all classes][org.springframework.batch.core.launch.support]
EMMA 2.0.5312 (C) Vladimir Roubtsov