All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.springframework.batch.core.launch.support.JobRegistryBackgroundJobRunner Maven / Gradle / Ivy

/*
 * Copyright 2006-2021 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.springframework.batch.core.launch.support;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.configuration.DuplicateJobException;
import org.springframework.batch.core.configuration.JobFactory;
import org.springframework.batch.core.configuration.JobRegistry;
import org.springframework.batch.core.configuration.support.DefaultJobLoader;
import org.springframework.batch.core.configuration.support.GenericApplicationContextFactory;
import org.springframework.batch.core.configuration.support.JobLoader;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.io.Resource;
import org.springframework.util.Assert;

/**
 * 

* Command line launcher for registering jobs with a {@link JobRegistry}. * Normally this will be used in conjunction with an external trigger for the * jobs registered, e.g. a JMX MBean wrapper for a {@link JobLauncher}, or a * Quartz trigger. *

* *

* With any launch of a batch job within Spring Batch, a Spring context * containing the {@link Job} has to be created. Using this launcher, the jobs * are all registered with a {@link JobRegistry} defined in a parent application * context. The jobs are then set up in child contexts. All dependencies of the * runner will then be satisfied by autowiring by type from the parent * application context. Default values are provided for all fields except the * {@link JobRegistry}. Therefore, if autowiring fails to set it then an * exception will be thrown. *

* * @author Dave Syer * */ public 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 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 List 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 getErrors() { synchronized (errors) { return new ArrayList<>(errors); } } private void register(String[] paths) throws DuplicateJobException, IOException { maybeCreateJobLoader(); for (int i = 0; i < paths.length; i++) { Resource[] resources = parentContext.getResources(paths[i]); for (int j = 0; j < resources.length; j++) { Resource path = resources[j]; 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); return; } /** * 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. * * Example usage: * *
	 * $ java -classpath ... JobRegistryBackgroundJobRunner job-registry-context.xml job1.xml job2.xml ...
	 * 
* * 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). * * 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 { 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(new Runnable() { @Override public void run() { 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(); } /** * 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(); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy