001/*
002 * Copyright 2012-2017 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.annotation.PostConstruct;
020import javax.persistence.EntityManagerFactory;
021import javax.sql.DataSource;
022
023import org.apache.commons.logging.Log;
024import org.apache.commons.logging.LogFactory;
025
026import org.springframework.batch.core.configuration.annotation.BatchConfigurer;
027import org.springframework.batch.core.explore.JobExplorer;
028import org.springframework.batch.core.explore.support.JobExplorerFactoryBean;
029import org.springframework.batch.core.launch.JobLauncher;
030import org.springframework.batch.core.launch.support.SimpleJobLauncher;
031import org.springframework.batch.core.repository.JobRepository;
032import org.springframework.batch.core.repository.support.JobRepositoryFactoryBean;
033import org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizers;
034import org.springframework.jdbc.datasource.DataSourceTransactionManager;
035import org.springframework.orm.jpa.JpaTransactionManager;
036import org.springframework.transaction.PlatformTransactionManager;
037import org.springframework.util.StringUtils;
038
039/**
040 * Basic {@link BatchConfigurer} implementation.
041 *
042 * @author Dave Syer
043 * @author Andy Wilkinson
044 * @author Kazuki Shimizu
045 */
046public class BasicBatchConfigurer implements BatchConfigurer {
047
048        private static final Log logger = LogFactory.getLog(BasicBatchConfigurer.class);
049
050        private final BatchProperties properties;
051
052        private final DataSource dataSource;
053
054        private final EntityManagerFactory entityManagerFactory;
055
056        private PlatformTransactionManager transactionManager;
057
058        private final TransactionManagerCustomizers transactionManagerCustomizers;
059
060        private JobRepository jobRepository;
061
062        private JobLauncher jobLauncher;
063
064        private JobExplorer jobExplorer;
065
066        /**
067         * Create a new {@link BasicBatchConfigurer} instance.
068         * @param properties the batch properties
069         * @param dataSource the underlying data source
070         * @param transactionManagerCustomizers transaction manager customizers (or
071         * {@code null})
072         */
073        protected BasicBatchConfigurer(BatchProperties properties, DataSource dataSource,
074                        TransactionManagerCustomizers transactionManagerCustomizers) {
075                this(properties, dataSource, null, transactionManagerCustomizers);
076        }
077
078        /**
079         * Create a new {@link BasicBatchConfigurer} instance.
080         * @param properties the batch properties
081         * @param dataSource the underlying data source
082         * @param entityManagerFactory the entity manager factory (or {@code null})
083         * @param transactionManagerCustomizers transaction manager customizers (or
084         * {@code null})
085         */
086        protected BasicBatchConfigurer(BatchProperties properties, DataSource dataSource,
087                        EntityManagerFactory entityManagerFactory,
088                        TransactionManagerCustomizers transactionManagerCustomizers) {
089                this.properties = properties;
090                this.entityManagerFactory = entityManagerFactory;
091                this.dataSource = dataSource;
092                this.transactionManagerCustomizers = transactionManagerCustomizers;
093        }
094
095        @Override
096        public JobRepository getJobRepository() {
097                return this.jobRepository;
098        }
099
100        @Override
101        public PlatformTransactionManager getTransactionManager() {
102                return this.transactionManager;
103        }
104
105        @Override
106        public JobLauncher getJobLauncher() {
107                return this.jobLauncher;
108        }
109
110        @Override
111        public JobExplorer getJobExplorer() throws Exception {
112                return this.jobExplorer;
113        }
114
115        @PostConstruct
116        public void initialize() {
117                try {
118                        this.transactionManager = createTransactionManager();
119                        this.jobRepository = createJobRepository();
120                        this.jobLauncher = createJobLauncher();
121                        this.jobExplorer = createJobExplorer();
122                }
123                catch (Exception ex) {
124                        throw new IllegalStateException("Unable to initialize Spring Batch", ex);
125                }
126        }
127
128        protected JobExplorer createJobExplorer() throws Exception {
129                JobExplorerFactoryBean jobExplorerFactoryBean = new JobExplorerFactoryBean();
130                jobExplorerFactoryBean.setDataSource(this.dataSource);
131                String tablePrefix = this.properties.getTablePrefix();
132                if (StringUtils.hasText(tablePrefix)) {
133                        jobExplorerFactoryBean.setTablePrefix(tablePrefix);
134                }
135                jobExplorerFactoryBean.afterPropertiesSet();
136                return jobExplorerFactoryBean.getObject();
137        }
138
139        protected JobLauncher createJobLauncher() throws Exception {
140                SimpleJobLauncher jobLauncher = new SimpleJobLauncher();
141                jobLauncher.setJobRepository(getJobRepository());
142                jobLauncher.afterPropertiesSet();
143                return jobLauncher;
144        }
145
146        protected JobRepository createJobRepository() throws Exception {
147                JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean();
148                factory.setDataSource(this.dataSource);
149                if (this.entityManagerFactory != null) {
150                        logger.warn(
151                                        "JPA does not support custom isolation levels, so locks may not be taken when launching Jobs");
152                        factory.setIsolationLevelForCreate("ISOLATION_DEFAULT");
153                }
154                String tablePrefix = this.properties.getTablePrefix();
155                if (StringUtils.hasText(tablePrefix)) {
156                        factory.setTablePrefix(tablePrefix);
157                }
158                factory.setTransactionManager(getTransactionManager());
159                factory.afterPropertiesSet();
160                return factory.getObject();
161        }
162
163        protected PlatformTransactionManager createTransactionManager() {
164                PlatformTransactionManager transactionManager = createAppropriateTransactionManager();
165                if (this.transactionManagerCustomizers != null) {
166                        this.transactionManagerCustomizers.customize(transactionManager);
167                }
168                return transactionManager;
169        }
170
171        private PlatformTransactionManager createAppropriateTransactionManager() {
172                if (this.entityManagerFactory != null) {
173                        return new JpaTransactionManager(this.entityManagerFactory);
174                }
175                return new DataSourceTransactionManager(this.dataSource);
176        }
177
178}