001/* 002 * Copyright 2012-2016 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.boot.autoconfigure.batch; 018 019import javax.persistence.EntityManagerFactory; 020import javax.sql.DataSource; 021 022import org.springframework.batch.core.configuration.ListableJobLocator; 023import org.springframework.batch.core.configuration.annotation.BatchConfigurer; 024import org.springframework.batch.core.converter.JobParametersConverter; 025import org.springframework.batch.core.explore.JobExplorer; 026import org.springframework.batch.core.explore.support.JobExplorerFactoryBean; 027import org.springframework.batch.core.launch.JobLauncher; 028import org.springframework.batch.core.launch.JobOperator; 029import org.springframework.batch.core.launch.support.SimpleJobOperator; 030import org.springframework.batch.core.repository.JobRepository; 031import org.springframework.beans.factory.ObjectProvider; 032import org.springframework.boot.ExitCodeGenerator; 033import org.springframework.boot.autoconfigure.AutoConfigureAfter; 034import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 035import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; 036import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 037import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 038import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 039import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration; 040import org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizers; 041import org.springframework.boot.context.properties.EnableConfigurationProperties; 042import org.springframework.context.annotation.Bean; 043import org.springframework.context.annotation.Configuration; 044import org.springframework.core.io.ResourceLoader; 045import org.springframework.jdbc.core.JdbcOperations; 046import org.springframework.transaction.PlatformTransactionManager; 047import org.springframework.util.StringUtils; 048 049/** 050 * {@link EnableAutoConfiguration Auto-configuration} for Spring Batch. By default a 051 * Runner will be created and all jobs in the context will be executed on startup. 052 * <p> 053 * Disable this behavior with {@literal spring.batch.job.enabled=false}). 054 * <p> 055 * Alternatively, discrete Job names to execute on startup can be supplied by the User 056 * with a comma-delimited list: {@literal spring.batch.job.names=job1,job2}. In this case 057 * the Runner will first find jobs registered as Beans, then those in the existing 058 * JobRegistry. 059 * 060 * @author Dave Syer 061 * @author Eddú Meléndez 062 * @author Kazuki Shimizu 063 */ 064@Configuration 065@ConditionalOnClass({ JobLauncher.class, DataSource.class, JdbcOperations.class }) 066@AutoConfigureAfter(HibernateJpaAutoConfiguration.class) 067@ConditionalOnBean(JobLauncher.class) 068@EnableConfigurationProperties(BatchProperties.class) 069public class BatchAutoConfiguration { 070 071 private final BatchProperties properties; 072 073 private final JobParametersConverter jobParametersConverter; 074 075 public BatchAutoConfiguration(BatchProperties properties, 076 ObjectProvider<JobParametersConverter> jobParametersConverter) { 077 this.properties = properties; 078 this.jobParametersConverter = jobParametersConverter.getIfAvailable(); 079 } 080 081 @Bean 082 @ConditionalOnMissingBean 083 @ConditionalOnBean(DataSource.class) 084 public BatchDatabaseInitializer batchDatabaseInitializer(DataSource dataSource, 085 ResourceLoader resourceLoader) { 086 return new BatchDatabaseInitializer(dataSource, resourceLoader, this.properties); 087 } 088 089 @Bean 090 @ConditionalOnMissingBean 091 @ConditionalOnProperty(prefix = "spring.batch.job", name = "enabled", havingValue = "true", matchIfMissing = true) 092 public JobLauncherCommandLineRunner jobLauncherCommandLineRunner( 093 JobLauncher jobLauncher, JobExplorer jobExplorer) { 094 JobLauncherCommandLineRunner runner = new JobLauncherCommandLineRunner( 095 jobLauncher, jobExplorer); 096 String jobNames = this.properties.getJob().getNames(); 097 if (StringUtils.hasText(jobNames)) { 098 runner.setJobNames(jobNames); 099 } 100 return runner; 101 } 102 103 @Bean 104 @ConditionalOnMissingBean(ExitCodeGenerator.class) 105 public JobExecutionExitCodeGenerator jobExecutionExitCodeGenerator() { 106 return new JobExecutionExitCodeGenerator(); 107 } 108 109 @Bean 110 @ConditionalOnMissingBean 111 @ConditionalOnBean(DataSource.class) 112 public JobExplorer jobExplorer(DataSource dataSource) throws Exception { 113 JobExplorerFactoryBean factory = new JobExplorerFactoryBean(); 114 factory.setDataSource(dataSource); 115 String tablePrefix = this.properties.getTablePrefix(); 116 if (StringUtils.hasText(tablePrefix)) { 117 factory.setTablePrefix(tablePrefix); 118 } 119 factory.afterPropertiesSet(); 120 return factory.getObject(); 121 } 122 123 @Bean 124 @ConditionalOnMissingBean(JobOperator.class) 125 public SimpleJobOperator jobOperator(JobExplorer jobExplorer, JobLauncher jobLauncher, 126 ListableJobLocator jobRegistry, JobRepository jobRepository) 127 throws Exception { 128 SimpleJobOperator factory = new SimpleJobOperator(); 129 factory.setJobExplorer(jobExplorer); 130 factory.setJobLauncher(jobLauncher); 131 factory.setJobRegistry(jobRegistry); 132 factory.setJobRepository(jobRepository); 133 if (this.jobParametersConverter != null) { 134 factory.setJobParametersConverter(this.jobParametersConverter); 135 } 136 return factory; 137 } 138 139 @EnableConfigurationProperties(BatchProperties.class) 140 @ConditionalOnClass(value = PlatformTransactionManager.class, name = "javax.persistence.EntityManagerFactory") 141 @ConditionalOnMissingBean(BatchConfigurer.class) 142 @Configuration 143 protected static class JpaBatchConfiguration { 144 145 private final BatchProperties properties; 146 147 protected JpaBatchConfiguration(BatchProperties properties) { 148 this.properties = properties; 149 } 150 151 // The EntityManagerFactory may not be discoverable by type when this condition 152 // is evaluated, so we need a well-known bean name. This is the one used by Spring 153 // Boot in the JPA auto configuration. 154 @Bean 155 @ConditionalOnBean(name = "entityManagerFactory") 156 public BasicBatchConfigurer jpaBatchConfigurer(DataSource dataSource, 157 EntityManagerFactory entityManagerFactory, 158 ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) { 159 return new BasicBatchConfigurer(this.properties, dataSource, 160 entityManagerFactory, transactionManagerCustomizers.getIfAvailable()); 161 } 162 163 @Bean 164 @ConditionalOnMissingBean(name = "entityManagerFactory") 165 public BasicBatchConfigurer basicBatchConfigurer(DataSource dataSource, 166 ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) { 167 return new BasicBatchConfigurer(this.properties, dataSource, 168 transactionManagerCustomizers.getIfAvailable()); 169 } 170 171 } 172 173}