class JobRegistryBackgroundJobRunner {

	/**
	 * System property key that switches the runner to "embedded" mode (returning
	 * immediately from the main method). Useful for testing purposes.
	 */
	public static final String EMBEDDED = JobRegistryBackgroundJobRunner.class.getSimpleName() + ".EMBEDDED";

	private static final Log logger = LogFactory.getLog(JobRegistryBackgroundJobRunner.class);

	private JobLoader jobLoader;

	private ApplicationContext parentContext = null;

	public static boolean testing = false;

	final private String parentContextPath;

	private JobRegistry jobRegistry;

	private static final List<Exception> errors = Collections.synchronizedList(new ArrayList<>());

	/**
	 * @param parentContextPath the parentContextPath to be used by the
	 * JobRegistryBackgroundJobRunner.
	 */
	public JobRegistryBackgroundJobRunner(String parentContextPath) {
		super();
		this.parentContextPath = parentContextPath;
	}

	/**
	 * A loader for the jobs that are going to be registered.
	 * @param jobLoader the {@link JobLoader} to set
	 */
	public void setJobLoader(JobLoader jobLoader) {
		this.jobLoader = jobLoader;
	}

	/**
	 * A job registry that can be used to create a job loader (if none is provided).
	 * @param jobRegistry the {@link JobRegistry} to set
	 */
	public void setJobRegistry(JobRegistry jobRegistry) {
		this.jobRegistry = jobRegistry;
	}

	/**
	 * Public getter for the startup errors encountered during parent context creation.
	 * @return the errors
	 */
	public static List<Exception> getErrors() {
		synchronized (errors) {
			return new ArrayList<>(errors);
		}
	}

	private void register(String[] paths) throws DuplicateJobException, IOException {

		maybeCreateJobLoader();

		for (String s : paths) {

			Resource[] resources = parentContext.getResources(s);

			for (Resource path : resources) {

				if (logger.isInfoEnabled()) {
					logger.info("Registering Job definitions from " + Arrays.toString(resources));
				}

				GenericApplicationContextFactory factory = new GenericApplicationContextFactory(path);
				factory.setApplicationContext(parentContext);
				jobLoader.load(factory);
			}

		}

	}

	/**
	 * If there is no {@link JobLoader} then try and create one from existing bean
	 * definitions.
	 */
	private void maybeCreateJobLoader() {

		if (jobLoader != null) {
			return;
		}

		String[] names = parentContext.getBeanNamesForType(JobLoader.class);
		if (names.length == 0) {
			if (parentContext.containsBean("jobLoader")) {
				jobLoader = parentContext.getBean("jobLoader", JobLoader.class);
				return;
			}
			if (jobRegistry != null) {
				jobLoader = new DefaultJobLoader(jobRegistry);
				return;
			}
		}

		jobLoader = parentContext.getBean(names[0], JobLoader.class);

	}

	/**
	 * Supply a list of application context locations, starting with the parent context,
	 * and followed by the children. The parent must contain a {@link JobRegistry} and the
	 * child contexts are expected to contain {@link Job} definitions, each of which will
	 * be registered wit the registry.
	 * <p>
	 * Example usage:
	 *
	 * <pre>
	 * $ java -classpath ... JobRegistryBackgroundJobRunner job-registry-context.xml job1.xml job2.xml ...
	 * </pre>
	 *
	 * The child contexts are created only when needed though the {@link JobFactory}
	 * interface (but the XML is validated on startup by using it to create a
	 * {@link BeanFactory} which is then discarded).
	 * <p>
	 * The parent context is created in a separate thread, and the program will pause for
	 * input in an infinite loop until the user hits any key.
	 * @param args the context locations to use (first one is for parent)
	 * @throws Exception if anything goes wrong with the context creation
	 */
	public static void main(String... args) throws Exception {<FILL_FUNCTION_BODY>}

	/**
	 * Unregister all the {@link Job} instances that were registered by this post
	 * processor.
	 * @see org.springframework.beans.factory.DisposableBean#destroy()
	 */
	private void destroy() throws Exception {
		jobLoader.clear();
	}

	private void run() {
		final ApplicationContext parent = new ClassPathXmlApplicationContext(parentContextPath);
		parent.getAutowireCapableBeanFactory()
			.autowireBeanProperties(this, AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, false);
		parent.getAutowireCapableBeanFactory().initializeBean(this, getClass().getSimpleName());
		this.parentContext = parent;
	}

	/**
	 * If embedded in a JVM, call this method to terminate the main method.
	 */
	public static void stop() {
		synchronized (JobRegistryBackgroundJobRunner.class) {
			JobRegistryBackgroundJobRunner.class.notify();
		}
	}

}


		Assert.state(args.length >= 1, "At least one argument (the parent context path) must be provided.");

		final JobRegistryBackgroundJobRunner launcher = new JobRegistryBackgroundJobRunner(args[0]);
		errors.clear();

		if (logger.isInfoEnabled()) {
			logger.info("Starting job registry in parent context from XML at: [" + args[0] + "]");
		}
		new Thread(() -> {
			try {
				launcher.run();
			}
			catch (RuntimeException e) {
				errors.add(e);
				throw e;
			}
		}).start();

		logger.info("Waiting for parent context to start.");
		while (launcher.parentContext == null && errors.isEmpty()) {
			Thread.sleep(100L);
		}

		synchronized (errors) {
			if (!errors.isEmpty()) {
				if (logger.isInfoEnabled()) {
					logger.info(errors.size() + " errors detected on startup of parent context.  Rethrowing.");
				}
				throw errors.get(0);
			}
		}
		errors.clear();

		// Paths to individual job configurations.
		final String[] paths = new String[args.length - 1];
		System.arraycopy(args, 1, paths, 0, paths.length);

		if (logger.isInfoEnabled()) {
			logger.info("Parent context started.  Registering jobs from paths: " + Arrays.asList(paths));
		}
		launcher.register(paths);

		if (System.getProperty(EMBEDDED) != null) {
			launcher.destroy();
			return;
		}

		synchronized (JobRegistryBackgroundJobRunner.class) {
			System.out.println(
					"Started application.  Interrupt (CTRL-C) or call JobRegistryBackgroundJobRunner.stop() to exit.");
			JobRegistryBackgroundJobRunner.class.wait();
		}
		launcher.destroy();

	
<no_super_class>