001/*
002 * Copyright 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.jsr;
017
018import java.util.Properties;
019
020import javax.batch.runtime.StepExecution;
021import javax.batch.runtime.context.JobContext;
022
023import org.springframework.batch.core.JobExecution;
024import org.springframework.batch.core.jsr.configuration.support.BatchPropertyContext;
025import org.springframework.batch.core.scope.context.StepSynchronizationManager;
026import org.springframework.beans.factory.FactoryBean;
027import org.springframework.beans.factory.FactoryBeanNotInitializedException;
028import org.springframework.beans.factory.annotation.Autowired;
029import org.springframework.util.Assert;
030
031/**
032 * Provides a single {@link JobContext} for each thread in a running job.
033 * Subsequent calls to {@link FactoryBean#getObject()} on the same thread will
034 * return the same instance.  The {@link JobContext} wraps a {@link JobExecution}
035 * which is obtained in one of two ways:
036 * <ul>
037 *   <li>The current step scope (getting it from the current {@link StepExecution}</li>
038 *   <li>The provided {@link JobExecution} via the {@link #setJobExecution(JobExecution)}
039 * </ul>
040 *
041 * @author Michael Minella
042 * @since 3.0
043 */
044public class JsrJobContextFactoryBean implements FactoryBean<JobContext> {
045
046        private JobExecution jobExecution;
047        @Autowired
048        private BatchPropertyContext propertyContext;
049
050        private static final ThreadLocal<JobContext> contextHolder = new ThreadLocal<JobContext>();
051
052        /* (non-Javadoc)
053         * @see org.springframework.beans.factory.FactoryBean#getObject()
054         */
055        @Override
056        public JobContext getObject() throws Exception {
057                return getCurrent();
058        }
059
060        /* (non-Javadoc)
061         * @see org.springframework.beans.factory.FactoryBean#getObjectType()
062         */
063        @Override
064        public Class<?> getObjectType() {
065                return JobContext.class;
066        }
067
068        /* (non-Javadoc)
069         * @see org.springframework.beans.factory.FactoryBean#isSingleton()
070         */
071        @Override
072        public boolean isSingleton() {
073                return false;
074        }
075
076        /**
077         * Used to provide {@link JobContext} instances to batch artifacts that
078         * are not within the scope of a given step.
079         *
080         * @param jobExecution set the current {@link JobExecution}
081         */
082        public void setJobExecution(JobExecution jobExecution) {
083                Assert.notNull(jobExecution, "A JobExecution is required");
084                this.jobExecution = jobExecution;
085        }
086
087        /**
088         * @param propertyContext the {@link BatchPropertyContext} to obtain job properties from
089         */
090        public void setBatchPropertyContext(BatchPropertyContext propertyContext) {
091                this.propertyContext = propertyContext;
092        }
093
094        /**
095         * Used to remove the {@link JobContext} for the current thread.  Not used via
096         * normal processing but useful for testing.
097         */
098        public void close() {
099                if(contextHolder.get() != null) {
100                        contextHolder.remove();
101                }
102        }
103
104        private JobContext getCurrent() {
105                if(contextHolder.get() == null) {
106                        JobExecution curJobExecution = null;
107
108                        if(StepSynchronizationManager.getContext() != null) {
109                                curJobExecution = StepSynchronizationManager.getContext().getStepExecution().getJobExecution();
110                        }
111
112                        if(curJobExecution != null) {
113                                jobExecution = curJobExecution;
114                        }
115
116                        if(jobExecution == null) {
117                                throw new FactoryBeanNotInitializedException("A JobExecution is required");
118                        }
119
120                        JsrJobContext jobContext = new JsrJobContext();
121                        jobContext.setJobExecution(jobExecution);
122
123                        if(propertyContext != null) {
124                                jobContext.setProperties(propertyContext.getJobProperties());
125                        } else {
126                                jobContext.setProperties(new Properties());
127                        }
128
129                        contextHolder.set(jobContext);
130                }
131
132                return contextHolder.get();
133        }
134}