001/*
002 * Copyright 2012-2013 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 *      https://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 */
016package org.springframework.batch.core.configuration.annotation;
017
018import org.aopalliance.intercept.MethodInterceptor;
019import org.aopalliance.intercept.MethodInvocation;
020import org.springframework.aop.framework.ProxyFactory;
021import org.springframework.aop.target.AbstractLazyCreationTargetSource;
022import org.springframework.batch.core.configuration.JobRegistry;
023import org.springframework.batch.core.configuration.support.MapJobRegistry;
024import org.springframework.batch.core.explore.JobExplorer;
025import org.springframework.batch.core.launch.JobLauncher;
026import org.springframework.batch.core.repository.JobRepository;
027import org.springframework.beans.factory.annotation.Autowired;
028import org.springframework.context.ApplicationContext;
029import org.springframework.context.annotation.Bean;
030import org.springframework.context.annotation.Configuration;
031import org.springframework.transaction.PlatformTransactionManager;
032
033import java.util.concurrent.atomic.AtomicReference;
034
035/**
036 * Base {@code Configuration} class providing common structure for enabling and using Spring Batch. Customization is
037 * available by implementing the {@link BatchConfigurer} interface. The main components are created as lazy proxies that
038 * only initialize when a method is called. This is to prevent (as much as possible) configuration cycles from
039 * developing when these components are needed in a configuration resource that itself provides a
040 * {@link BatchConfigurer}.
041 *
042 * @author Dave Syer
043 * @since 2.2
044 * @see EnableBatchProcessing
045 */
046@Configuration
047public class SimpleBatchConfiguration extends AbstractBatchConfiguration {
048
049        @Autowired
050        private ApplicationContext context;
051
052        private boolean initialized = false;
053
054        private AtomicReference<JobRepository> jobRepository = new AtomicReference<JobRepository>();
055
056        private AtomicReference<JobLauncher> jobLauncher = new AtomicReference<JobLauncher>();
057
058        private AtomicReference<JobRegistry> jobRegistry = new AtomicReference<JobRegistry>();
059
060        private AtomicReference<PlatformTransactionManager> transactionManager = new AtomicReference<PlatformTransactionManager>();
061
062        private AtomicReference<JobExplorer> jobExplorer = new AtomicReference<JobExplorer>();
063
064        @Override
065        @Bean
066        public JobRepository jobRepository() throws Exception {
067                return createLazyProxy(jobRepository, JobRepository.class);
068        }
069
070        @Override
071        @Bean
072        public JobLauncher jobLauncher() throws Exception {
073                return createLazyProxy(jobLauncher, JobLauncher.class);
074        }
075
076        @Override
077        @Bean
078        public JobRegistry jobRegistry() throws Exception {
079                return createLazyProxy(jobRegistry, JobRegistry.class);
080        }
081
082        @Override
083        @Bean
084        public JobExplorer jobExplorer() {
085                return createLazyProxy(jobExplorer, JobExplorer.class);
086        }
087
088        @Override
089        @Bean
090        public PlatformTransactionManager transactionManager() throws Exception {
091                return createLazyProxy(transactionManager, PlatformTransactionManager.class);
092        }
093
094        private <T> T createLazyProxy(AtomicReference<T> reference, Class<T> type) {
095                ProxyFactory factory = new ProxyFactory();
096                factory.setTargetSource(new ReferenceTargetSource<T>(reference));
097                factory.addAdvice(new PassthruAdvice());
098                factory.setInterfaces(new Class<?>[] { type });
099                @SuppressWarnings("unchecked")
100                T proxy = (T) factory.getProxy();
101                return proxy;
102        }
103
104        /**
105         * Sets up the basic components by extracting them from the {@link BatchConfigurer configurer}, defaulting to some
106         * sensible values as long as a unique DataSource is available.
107         *
108         * @throws Exception if there is a problem in the configurer
109         */
110        protected void initialize() throws Exception {
111                if (initialized) {
112                        return;
113                }
114                BatchConfigurer configurer = getConfigurer(context.getBeansOfType(BatchConfigurer.class).values());
115                jobRepository.set(configurer.getJobRepository());
116                jobLauncher.set(configurer.getJobLauncher());
117                transactionManager.set(configurer.getTransactionManager());
118                jobRegistry.set(new MapJobRegistry());
119                jobExplorer.set(configurer.getJobExplorer());
120                initialized = true;
121        }
122
123        private class PassthruAdvice implements MethodInterceptor {
124
125                @Override
126                public Object invoke(MethodInvocation invocation) throws Throwable {
127                        return invocation.proceed();
128                }
129
130        }
131
132        private class ReferenceTargetSource<T> extends AbstractLazyCreationTargetSource {
133
134                private AtomicReference<T> reference;
135
136                public ReferenceTargetSource(AtomicReference<T> reference) {
137                        this.reference = reference;
138                }
139
140                @Override
141                protected Object createObject() throws Exception {
142                        initialize();
143                        return reference.get();
144                }
145        }
146
147}