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}