BATCH-2002: Initial support for complex conditional replacements

This commit is contained in:
Chris Schaefer
2013-09-08 19:59:35 -04:00
committed by Michael Minella
parent 65d2df652a
commit e1f6de38e0
4 changed files with 123 additions and 8 deletions

View File

@@ -57,7 +57,6 @@ 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.BeanExpressionResolver;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.expression.StandardBeanExpressionResolver;
@@ -74,11 +73,9 @@ import org.springframework.util.ReflectionUtils;
* @since 3.0
*/
public class BatchPropertyBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware {
private JsrExpressionParser jsrExpressionParser;
private BatchPropertyContext batchPropertyContext;
private Log logger = LogFactory.getLog(getClass());
private ConfigurableListableBeanFactory beanFactory;
private BeanExpressionContext beanExpressionContext;
private BeanExpressionResolver expressionResolver = new StandardBeanExpressionResolver();
private Set<Class<? extends Annotation>> requiredAnnotations = new HashSet<Class<? extends Annotation>>();
public BatchPropertyBeanPostProcessor() {
@@ -189,7 +186,7 @@ public class BatchPropertyBeanPostProcessor implements BeanPostProcessor, BeanFa
if (batchArtifactProperties.containsKey(propertyKey)) {
String propertyValue = (String) batchArtifactProperties.get(propertyKey);
return (String) expressionResolver.evaluate(propertyValue, beanExpressionContext);
return jsrExpressionParser.parseExpression(propertyValue);
}
return null;
@@ -221,8 +218,12 @@ public class BatchPropertyBeanPostProcessor implements BeanPostProcessor, BeanFa
"BatchPropertyBeanPostProcessor requires a ConfigurableListableBeanFactory");
}
this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
this.beanExpressionContext = new BeanExpressionContext(this.beanFactory, this.beanFactory.getBean(StepScope.class));
ConfigurableListableBeanFactory configurableListableBeanFactory = (ConfigurableListableBeanFactory) beanFactory;
BeanExpressionContext beanExpressionContext = new BeanExpressionContext(configurableListableBeanFactory,
configurableListableBeanFactory.getBean(StepScope.class));
this.jsrExpressionParser = new JsrExpressionParser(new StandardBeanExpressionResolver(), beanExpressionContext);
}
@Autowired

View File

@@ -0,0 +1,99 @@
/*
* Copyright 2013 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.configuration.support;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.springframework.beans.factory.config.BeanExpressionContext;
import org.springframework.beans.factory.config.BeanExpressionResolver;
import org.springframework.util.StringUtils;
/**
* <p>
* Support class for parsing JSR-352 expressions. The JSR-352 expression syntax, for
* example conditional/elvis statements need to be massaged a bit to work as SPeL expressions.
* </p>
*
* @author Chris Schaefer
* @since 3.0
*/
public class JsrExpressionParser {
public static final String QUOTE = "'";
private static final String ELVIS_RHS = ":";
private static final String ELVIS_LHS = "\\?";
private static final String ELVIS_OPERATOR = "?:";
private static final String EXPRESSION_SUFFIX = "}";
private static final String EXPRESSION_PREFIX = "#{";
private static final String DEFAULT_VALUE_SEPARATOR = ";";
private static final Pattern CONDITIONAL_EXPRESSION = Pattern.compile("(#\\{\\w[^;]+)");
private BeanExpressionContext beanExpressionContext;
private BeanExpressionResolver beanExpressionResolver;
/**
* <p>
* Creates a new instances of this expression parser with the provided expression resolver and context to evaluate
* against.
* </p>
*
* @param beanExpressionResolver the expression resolver to use when resolving expressions
* @param beanExpressionContext the expression context to resolve expressions against
*/
public JsrExpressionParser(BeanExpressionResolver beanExpressionResolver, BeanExpressionContext beanExpressionContext) {
this.beanExpressionContext = beanExpressionContext;
this.beanExpressionResolver = beanExpressionResolver;
}
/**
* <p>
* Parses the provided expression, applying any transformations needed to evaluate as a SPeL expression.
* </p>
*
* @param expression the expression to parse and transform
* @return a JSR-352 transformed expression that can be evaluated by a SPeL parser
*/
public String parseExpression(String expression) {
if (StringUtils.countOccurrencesOf(expression, ELVIS_OPERATOR) > 0) {
String expressionToParse = expression;
Matcher conditionalExpressionMatcher = CONDITIONAL_EXPRESSION.matcher(expressionToParse);
while (conditionalExpressionMatcher.find()) {
String conditionalExpression = conditionalExpressionMatcher.group(1);
String value = conditionalExpression.split(ELVIS_LHS)[0];
String defaultValue = conditionalExpression.split(ELVIS_RHS)[1];
StringBuilder parsedExpression = new StringBuilder()
.append(EXPRESSION_PREFIX)
.append((String) beanExpressionResolver.evaluate(value, beanExpressionContext))
.append(ELVIS_OPERATOR)
.append(QUOTE)
.append((String) beanExpressionResolver.evaluate(defaultValue, beanExpressionContext))
.append(QUOTE)
.append(EXPRESSION_SUFFIX);
expressionToParse = expressionToParse.replace(conditionalExpression, parsedExpression);
}
expressionToParse = expressionToParse.replace(DEFAULT_VALUE_SEPARATOR, "");
return (String) beanExpressionResolver.evaluate(expressionToParse, beanExpressionContext);
}
return (String) beanExpressionResolver.evaluate(expression, beanExpressionContext);
}
}

View File

@@ -56,7 +56,10 @@ public class JobPropertySubstitutionTests {
@Test
public void testPropertySubstitutionSimple() throws Exception {
JobExecution jobExecution = jobLauncher.run(job,
new JobParametersBuilder().addString("testParam", "testParamValue").toJobParameters());
new JobParametersBuilder()
.addString("testParam", "testParamValue")
.addString("file.name.junit", "myfile2")
.toJobParameters());
assertEquals(ExitStatus.COMPLETED, jobExecution.getExitStatus());
}
@@ -122,9 +125,19 @@ public class JobPropertySubstitutionTests {
@BatchProperty
String processorProperty1;
@Inject
@BatchProperty
String processorProperty2;
@Inject
@BatchProperty
String processorProperty3;
@Override
public Object processItem(Object item) throws Exception {
assertEquals("testParamValue", processorProperty1);
assertEquals("myfile1.txt", processorProperty2);
assertEquals("/myfile2.txt", processorProperty3);
return item;
}

View File

@@ -22,6 +22,8 @@
<processor ref="testProcessor">
<properties>
<property name="processorProperty1" value="#{jobParameters['testParam']}"/>
<property name="processorProperty2" value="#{jobParameters['unresolving.prop']}?:myfile1.txt;"/>
<property name="processorProperty3" value="#{jobParameters['unresolving.prop']}?:#{systemProperties['file.separator']};#{jobParameters['infile.name']}?:#{jobParameters['file.name.junit']};.txt" />
</properties>
</processor>
<writer ref="testWriter">