AutomaticJobRegistrarConfigurationSupport.java

package com.namics.oss.spring.support.batch.autoconfigure;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.batch.core.configuration.support.AutomaticJobRegistrar;
import org.springframework.batch.core.configuration.support.GenericApplicationContextFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.util.SystemPropertyUtils;

import javax.inject.Inject;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import static java.lang.Class.forName;
import static org.springframework.util.ClassUtils.convertClassNameToResourcePath;
import static org.springframework.util.StringUtils.hasText;

/**
 * AutomaticJobRegistrarConfigurationSupport.
 * This configuration looks for jobs in a modular fashion, meaning that every job configuration file gets its own
 * Child-ApplicationContext.
 *
 * @author lboesch, Namics AG
 * @since 03.07.17 15:38
 */
@Configuration
@EnableConfigurationProperties(SpringBatchSupportProperties.class)
@ConditionalOnMissingBean({ SpringBatchSupportAutoConfiguration.class })
public class AutomaticJobRegistrarConfigurationSupport extends SpringBatchSupportAutoConfiguration {

	private static final Logger LOGGER = LoggerFactory.getLogger(AutomaticJobRegistrarConfigurationSupport.class);

	private static final String DEFAULT_PACKAGE_TO_SCAN = "spring.batch.jobs";

	@Inject
	protected SpringBatchSupportProperties springBatchSupportProperties;


	@Override
	protected void addApplicationContextFactories(AutomaticJobRegistrar automaticJobRegistrar) throws Exception {
		registerJobsFromJavaConfig(automaticJobRegistrar);
	}


	protected void registerJobsFromJavaConfig(AutomaticJobRegistrar automaticJobRegistrar) throws ClassNotFoundException, IOException {
		List<Class<?>> classes = findMyTypes(getDefaultPackageToScanForBatchJobs());
		for (Class<?> clazz : classes) {
			LOGGER.info("Register jobs from {}", clazz);
			automaticJobRegistrar.addApplicationContextFactory(new GenericApplicationContextFactory(clazz));
		}
	}

	protected List<Class<?>> findMyTypes(String basePackage) throws IOException, ClassNotFoundException {
		ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
		MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resourcePatternResolver);

		List<Class<?>> candidates = new ArrayList<>();
		String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + resolveBasePackage(basePackage)
		                           + "/" + "**/*.class";
		Resource[] resources = resourcePatternResolver.getResources(packageSearchPath);
		for (Resource resource : resources) {
			if (resource.isReadable()) {
				MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(resource);
				if (isCandidate(metadataReader)) {
					candidates.add(forName(metadataReader.getClassMetadata().getClassName()));
				}
			}
		}
		return candidates;
	}

	protected String resolveBasePackage(String basePackage) {
		return convertClassNameToResourcePath(SystemPropertyUtils.resolvePlaceholders(basePackage));
	}

	protected boolean isCandidate(MetadataReader metadataReader) throws ClassNotFoundException {
		try {
			Class<?> c = forName(metadataReader.getClassMetadata().getClassName());
			if (c.getAnnotation(Configuration.class) != null) {
				return true;
			}
		} catch (Throwable e) {
			LOGGER.debug("error {}", e);
		}
		return false;
	}

	protected String getDefaultPackageToScanForBatchJobs() {
		if (hasText(springBatchSupportProperties.getJobConfiguration().getPackageName())) {
			return springBatchSupportProperties.getJobConfiguration().getPackageName();
		}
		return DEFAULT_PACKAGE_TO_SCAN;
	}


}