org.springframework.batch.core.jsr.launch.support.BatchPropertyBeanPostProcessor.java Source code

Java tutorial

Introduction

Here is the source code for org.springframework.batch.core.jsr.launch.support.BatchPropertyBeanPostProcessor.java

Source

/*
 * Copyright 2013-2014 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.springframework.batch.core.jsr.launch.support;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;

import javax.batch.api.BatchProperty;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.batch.core.jsr.configuration.support.BatchPropertyContext;
import org.springframework.batch.core.jsr.configuration.support.JsrExpressionParser;
import org.springframework.batch.core.scope.StepScope;
import org.springframework.batch.core.scope.context.StepContext;
import org.springframework.batch.core.scope.context.StepSynchronizationManager;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanExpressionContext;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.expression.StandardBeanExpressionResolver;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;

/**
 * <p>
 * {@link BeanPostProcessor} implementation used to inject JSR-352 String properties into batch artifact fields
 * that are marked with the {@link BatchProperty} annotation.
 * </p>
 *
 * @author Chris Schaefer
 * @author Michael Minella
 * @since 3.0
 */
@SuppressWarnings("unchecked")
public class BatchPropertyBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware {
    private static final String SCOPED_TARGET_BEAN_PREFIX = "scopedTarget.";
    private static final Log LOGGER = LogFactory.getLog(BatchPropertyBeanPostProcessor.class);
    private static final Set<Class<? extends Annotation>> REQUIRED_ANNOTATIONS = new HashSet<Class<? extends Annotation>>();

    private JsrExpressionParser jsrExpressionParser;
    private BatchPropertyContext batchPropertyContext;

    static {
        ClassLoader cl = BatchPropertyBeanPostProcessor.class.getClassLoader();

        try {
            REQUIRED_ANNOTATIONS.add((Class<? extends Annotation>) cl.loadClass("javax.inject.Inject"));
        } catch (ClassNotFoundException ex) {
            LOGGER.warn("javax.inject.Inject not found - @BatchProperty marked fields will not be processed.");
        }

        REQUIRED_ANNOTATIONS.add(BatchProperty.class);
    }

    @Override
    public Object postProcessBeforeInitialization(final Object artifact, String artifactName)
            throws BeansException {
        Properties artifactProperties = getArtifactProperties(artifactName);

        if (artifactProperties.isEmpty()) {
            return artifact;
        }

        injectBatchProperties(artifact, artifactProperties);

        return artifact;
    }

    @Override
    public Object postProcessAfterInitialization(Object artifact, String artifactName) throws BeansException {
        return artifact;
    }

    private Properties getArtifactProperties(String artifactName) {
        String originalArtifactName = artifactName;

        if (originalArtifactName.startsWith(SCOPED_TARGET_BEAN_PREFIX)) {
            originalArtifactName = artifactName.substring(SCOPED_TARGET_BEAN_PREFIX.length());
        }

        StepContext stepContext = StepSynchronizationManager.getContext();

        if (stepContext != null) {
            return batchPropertyContext.getStepArtifactProperties(stepContext.getStepName(), originalArtifactName);
        }

        return batchPropertyContext.getArtifactProperties(originalArtifactName);
    }

    private void injectBatchProperties(final Object artifact, final Properties artifactProperties) {
        ReflectionUtils.doWithFields(artifact.getClass(), new ReflectionUtils.FieldCallback() {
            @Override
            public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
                if (isValidFieldModifier(field) && isAnnotated(field)) {
                    boolean isAccessible = field.isAccessible();
                    field.setAccessible(true);

                    String batchProperty = getBatchPropertyFieldValue(field, artifactProperties);

                    if (StringUtils.hasText(batchProperty)) {
                        field.set(artifact, batchProperty);
                    }

                    field.setAccessible(isAccessible);
                }
            }
        });
    }

    private String getBatchPropertyFieldValue(Field field, Properties batchArtifactProperties) {
        BatchProperty batchProperty = field.getAnnotation(BatchProperty.class);

        if (!"".equals(batchProperty.name())) {
            return getBatchProperty(batchProperty.name(), batchArtifactProperties);
        }

        return getBatchProperty(field.getName(), batchArtifactProperties);
    }

    private String getBatchProperty(String propertyKey, Properties batchArtifactProperties) {
        if (batchArtifactProperties.containsKey(propertyKey)) {
            String propertyValue = (String) batchArtifactProperties.get(propertyKey);

            return jsrExpressionParser.parseExpression(propertyValue);
        }

        return null;
    }

    private boolean isAnnotated(Field field) {
        for (Class<? extends Annotation> annotation : REQUIRED_ANNOTATIONS) {
            if (!field.isAnnotationPresent(annotation)) {
                return false;
            }
        }

        return true;
    }

    private boolean isValidFieldModifier(Field field) {
        return !Modifier.isStatic(field.getModifiers()) && !Modifier.isFinal(field.getModifiers());
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        if (!(beanFactory instanceof ConfigurableListableBeanFactory)) {
            throw new IllegalArgumentException(
                    "BatchPropertyBeanPostProcessor requires a ConfigurableListableBeanFactory");
        }

        ConfigurableListableBeanFactory configurableListableBeanFactory = (ConfigurableListableBeanFactory) beanFactory;

        BeanExpressionContext beanExpressionContext = new BeanExpressionContext(configurableListableBeanFactory,
                configurableListableBeanFactory.getBean(StepScope.class));

        this.jsrExpressionParser = new JsrExpressionParser(new StandardBeanExpressionResolver(),
                beanExpressionContext);
    }

    @Autowired
    public void setBatchPropertyContext(BatchPropertyContext batchPropertyContext) {
        this.batchPropertyContext = batchPropertyContext;
    }
}