General cleanup etc around properties and substitution
* Move pre-context init of JobParameters support out of ThreadLocalClassloaderBeanPostProcessor * General cleanup in BatchPropertyBeanPostProcessor, move artifact type checking into classifer class, minor naming updates * Refactor BatchPropertyContext to use different data structures for different types of properties and provide named accessors * Remove path based artifact scoping support * Make sure expressions are parsed when resolving properties directly from XML * Update property tests * Move registration of jobProperties bean into the JsrNamespaceUtils
This commit is contained in:
@@ -33,19 +33,18 @@ import org.springframework.util.Assert;
|
||||
* @since 3.0
|
||||
*/
|
||||
public class JobContext implements javax.batch.runtime.context.JobContext {
|
||||
|
||||
private Object transientUserData;
|
||||
private Properties properties;
|
||||
private JobExecution jobExecution;
|
||||
private Properties properties;
|
||||
private JobExecution jobExecution;
|
||||
|
||||
/**
|
||||
/**
|
||||
* @param jobExecution for the related job
|
||||
*/
|
||||
public JobContext(JobExecution jobExecution, Properties properties) {
|
||||
Assert.notNull(jobExecution, "A JobExecution is required");
|
||||
|
||||
this.properties = properties;
|
||||
this.jobExecution = jobExecution;
|
||||
this.jobExecution = jobExecution;
|
||||
this.properties = properties != null ? properties : new Properties();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
@@ -93,7 +92,7 @@ public class JobContext implements javax.batch.runtime.context.JobContext {
|
||||
*/
|
||||
@Override
|
||||
public Properties getProperties() {
|
||||
return properties != null ? properties : new Properties();
|
||||
return properties;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
|
||||
@@ -41,9 +41,7 @@ public class StepContextFactoryBean implements FactoryBean<StepContext> {
|
||||
public StepContext getObject() throws Exception {
|
||||
org.springframework.batch.core.scope.context.StepContext stepContext = StepSynchronizationManager.getContext();
|
||||
|
||||
String stepName = stepContext.getStepName();
|
||||
String jobName = stepContext.getStepExecution().getJobExecution().getJobInstance().getJobName();
|
||||
Properties properties = batchPropertyContext.getStepLevelProperties(jobName + "." + stepName);
|
||||
Properties properties = batchPropertyContext.getStepProperties(stepContext.getStepName());
|
||||
|
||||
return new StepContext(stepContext.getStepExecution(), properties);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* 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 javax.batch.api.Batchlet;
|
||||
import javax.batch.api.Decider;
|
||||
import javax.batch.api.chunk.CheckpointAlgorithm;
|
||||
import javax.batch.api.chunk.ItemProcessor;
|
||||
import javax.batch.api.chunk.ItemReader;
|
||||
import javax.batch.api.chunk.ItemWriter;
|
||||
import javax.batch.api.chunk.listener.ChunkListener;
|
||||
import javax.batch.api.chunk.listener.ItemProcessListener;
|
||||
import javax.batch.api.chunk.listener.ItemReadListener;
|
||||
import javax.batch.api.chunk.listener.ItemWriteListener;
|
||||
import javax.batch.api.chunk.listener.RetryProcessListener;
|
||||
import javax.batch.api.chunk.listener.RetryReadListener;
|
||||
import javax.batch.api.chunk.listener.RetryWriteListener;
|
||||
import javax.batch.api.chunk.listener.SkipProcessListener;
|
||||
import javax.batch.api.chunk.listener.SkipReadListener;
|
||||
import javax.batch.api.chunk.listener.SkipWriteListener;
|
||||
import javax.batch.api.listener.JobListener;
|
||||
import javax.batch.api.listener.StepListener;
|
||||
import javax.batch.api.partition.PartitionAnalyzer;
|
||||
import javax.batch.api.partition.PartitionCollector;
|
||||
import javax.batch.api.partition.PartitionMapper;
|
||||
import javax.batch.api.partition.PartitionPlan;
|
||||
import javax.batch.api.partition.PartitionReducer;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Simple enum representing metadata about batch artifacts and types.
|
||||
* </p>
|
||||
*
|
||||
* @author Chris Schaefer
|
||||
*/
|
||||
public enum BatchArtifact {
|
||||
ITEM_READER(ItemReader.class),
|
||||
ITEM_WRITER(ItemWriter.class),
|
||||
ITEM_PROCESSOR(ItemProcessor.class),
|
||||
CHECKPOINT_ALGORITHM(CheckpointAlgorithm.class),
|
||||
BATCHLET(Batchlet.class),
|
||||
ITEM_READ_LISTENER(ItemReadListener.class),
|
||||
ITEM_PROCESS_LISTENER(ItemProcessListener.class),
|
||||
ITEM_WRITE_LISTENER(ItemWriteListener.class),
|
||||
JOB_LISTENER(JobListener.class),
|
||||
STEP_LISTENER(StepListener.class),
|
||||
CHUNK_LISTENER(ChunkListener.class),
|
||||
SKIP_READ_LISTENER(SkipReadListener.class),
|
||||
SKIP_PROCESS_LISTENER(SkipProcessListener.class),
|
||||
SKIP_WRITER_LISTENER(SkipWriteListener.class),
|
||||
RETRY_READ_LISTENER(RetryReadListener.class),
|
||||
RETRY_PROCESS_LISTENER(RetryProcessListener.class),
|
||||
RETRY_WRITE_LISTENER(RetryWriteListener.class),
|
||||
PARTITION_MAPPER(PartitionMapper.class),
|
||||
PARTITION_REDUCER(PartitionReducer.class),
|
||||
PARTITION_COLLECTOR(PartitionCollector.class),
|
||||
PARTITION_ANALYZER(PartitionAnalyzer.class),
|
||||
PARTITION_PLAN(PartitionPlan.class),
|
||||
DECIDER(Decider.class);
|
||||
|
||||
private Class<?> clazz;
|
||||
|
||||
private BatchArtifact(Class<?> clazz) {
|
||||
this.clazz = clazz;
|
||||
}
|
||||
|
||||
private Class<?> getBatchArtifactClass() {
|
||||
return clazz;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Determines if the provided artifact is a JSR-352 batch artifact.
|
||||
* </p>
|
||||
*
|
||||
* @param artifact the artifact to check
|
||||
* @return boolean answer based on check
|
||||
*/
|
||||
public static boolean isBatchArtifact(Object artifact) {
|
||||
for (BatchArtifact batchArtifactType : BatchArtifact.values()) {
|
||||
if (batchArtifactType.getBatchArtifactClass().isInstance(artifact)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Enum to identify batch artifact types.
|
||||
* </p>
|
||||
*
|
||||
* @author Chris Schaefer
|
||||
* @since 3.0
|
||||
*/
|
||||
public enum BatchArtifactType {
|
||||
STEP,
|
||||
STEP_ARTIFACT,
|
||||
ARTIFACT,
|
||||
JOB
|
||||
}
|
||||
}
|
||||
@@ -23,29 +23,6 @@ import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.batch.api.BatchProperty;
|
||||
import javax.batch.api.Batchlet;
|
||||
import javax.batch.api.Decider;
|
||||
import javax.batch.api.chunk.CheckpointAlgorithm;
|
||||
import javax.batch.api.chunk.ItemProcessor;
|
||||
import javax.batch.api.chunk.ItemReader;
|
||||
import javax.batch.api.chunk.ItemWriter;
|
||||
import javax.batch.api.chunk.listener.ChunkListener;
|
||||
import javax.batch.api.chunk.listener.ItemProcessListener;
|
||||
import javax.batch.api.chunk.listener.ItemReadListener;
|
||||
import javax.batch.api.chunk.listener.ItemWriteListener;
|
||||
import javax.batch.api.chunk.listener.RetryProcessListener;
|
||||
import javax.batch.api.chunk.listener.RetryReadListener;
|
||||
import javax.batch.api.chunk.listener.RetryWriteListener;
|
||||
import javax.batch.api.chunk.listener.SkipProcessListener;
|
||||
import javax.batch.api.chunk.listener.SkipReadListener;
|
||||
import javax.batch.api.chunk.listener.SkipWriteListener;
|
||||
import javax.batch.api.listener.JobListener;
|
||||
import javax.batch.api.listener.StepListener;
|
||||
import javax.batch.api.partition.PartitionAnalyzer;
|
||||
import javax.batch.api.partition.PartitionCollector;
|
||||
import javax.batch.api.partition.PartitionMapper;
|
||||
import javax.batch.api.partition.PartitionPlan;
|
||||
import javax.batch.api.partition.PartitionReducer;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
@@ -73,87 +50,61 @@ import org.springframework.util.ReflectionUtils;
|
||||
* @since 3.0
|
||||
*/
|
||||
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;
|
||||
private Log logger = LogFactory.getLog(getClass());
|
||||
private Set<Class<? extends Annotation>> requiredAnnotations = new HashSet<Class<? extends Annotation>>();
|
||||
|
||||
public BatchPropertyBeanPostProcessor() {
|
||||
setRequiredAnnotations();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {
|
||||
if (!isBatchArtifact(bean)) {
|
||||
return bean;
|
||||
}
|
||||
|
||||
String beanPropertyName = getBeanPropertyName(beanName);
|
||||
|
||||
final Properties artifactProperties = batchPropertyContext.getBatchProperties(beanPropertyName);
|
||||
|
||||
if (artifactProperties.isEmpty()) {
|
||||
return bean;
|
||||
}
|
||||
|
||||
injectBatchProperties(bean, artifactProperties);
|
||||
|
||||
return bean;
|
||||
}
|
||||
|
||||
private String getBeanPropertyName(String beanName) {
|
||||
StepContext stepContext = StepSynchronizationManager.getContext();
|
||||
|
||||
if(stepContext != null) {
|
||||
String stepName = stepContext.getStepName();
|
||||
String jobName = stepContext.getStepExecution().getJobExecution().getJobInstance().getJobName();
|
||||
return jobName + "." + stepName + "." + beanName.substring("scopedTarget.".length());
|
||||
}
|
||||
|
||||
return beanName;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void setRequiredAnnotations() {
|
||||
static {
|
||||
ClassLoader cl = BatchPropertyBeanPostProcessor.class.getClassLoader();
|
||||
|
||||
try {
|
||||
requiredAnnotations.add((Class<? extends Annotation>) cl.loadClass("javax.inject.Inject"));
|
||||
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.");
|
||||
LOGGER.warn("javax.inject.Inject not found - @BatchProperty marked fields will not be processed.");
|
||||
}
|
||||
|
||||
requiredAnnotations.add(BatchProperty.class);
|
||||
REQUIRED_ANNOTATIONS.add(BatchProperty.class);
|
||||
}
|
||||
|
||||
private boolean isBatchArtifact(Object bean) {
|
||||
return (bean instanceof ItemReader) ||
|
||||
(bean instanceof ItemProcessor) ||
|
||||
(bean instanceof ItemWriter) ||
|
||||
(bean instanceof CheckpointAlgorithm) ||
|
||||
(bean instanceof Batchlet) ||
|
||||
(bean instanceof ItemReadListener) ||
|
||||
(bean instanceof ItemProcessListener) ||
|
||||
(bean instanceof ItemWriteListener) ||
|
||||
(bean instanceof JobListener) ||
|
||||
(bean instanceof StepListener) ||
|
||||
(bean instanceof ChunkListener) ||
|
||||
(bean instanceof SkipReadListener) ||
|
||||
(bean instanceof SkipProcessListener) ||
|
||||
(bean instanceof SkipWriteListener) ||
|
||||
(bean instanceof RetryReadListener) ||
|
||||
(bean instanceof RetryProcessListener) ||
|
||||
(bean instanceof RetryWriteListener) ||
|
||||
(bean instanceof PartitionMapper) ||
|
||||
(bean instanceof PartitionReducer) ||
|
||||
(bean instanceof PartitionCollector) ||
|
||||
(bean instanceof PartitionAnalyzer) ||
|
||||
(bean instanceof PartitionPlan) ||
|
||||
(bean instanceof Decider);
|
||||
@Override
|
||||
public Object postProcessBeforeInitialization(final Object artifact, String artifactName) throws BeansException {
|
||||
if (! BatchArtifact.isBatchArtifact(artifact)) {
|
||||
return artifact;
|
||||
}
|
||||
|
||||
Properties artifactProperties = getArtifactProperties(artifactName);
|
||||
|
||||
if (artifactProperties.isEmpty()) {
|
||||
return artifact;
|
||||
}
|
||||
|
||||
injectBatchProperties(artifact, artifactProperties);
|
||||
|
||||
return artifact;
|
||||
}
|
||||
|
||||
private void injectBatchProperties(final Object bean, final Properties artifactProperties) {
|
||||
ReflectionUtils.doWithFields(bean.getClass(), new ReflectionUtils.FieldCallback() {
|
||||
@Override
|
||||
public Object postProcessAfterInitialization(Object artifact, String artifactName) throws BeansException {
|
||||
return artifact;
|
||||
}
|
||||
|
||||
private Properties getArtifactProperties(String artifactName) {
|
||||
StepContext stepContext = StepSynchronizationManager.getContext();
|
||||
|
||||
if (stepContext != null) {
|
||||
String originalArtifactName = artifactName.substring(SCOPED_TARGET_BEAN_PREFIX.length());
|
||||
|
||||
return batchPropertyContext.getStepArtifactProperties(stepContext.getStepName(), originalArtifactName);
|
||||
}
|
||||
|
||||
return batchPropertyContext.getArtifactProperties(artifactName);
|
||||
}
|
||||
|
||||
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)) {
|
||||
@@ -163,7 +114,7 @@ public class BatchPropertyBeanPostProcessor implements BeanPostProcessor, BeanFa
|
||||
String batchProperty = getBatchPropertyFieldValue(field, artifactProperties);
|
||||
|
||||
if (batchProperty != null) {
|
||||
field.set(bean, batchProperty);
|
||||
field.set(artifact, batchProperty);
|
||||
}
|
||||
|
||||
field.setAccessible(isAccessible);
|
||||
@@ -193,7 +144,7 @@ public class BatchPropertyBeanPostProcessor implements BeanPostProcessor, BeanFa
|
||||
}
|
||||
|
||||
private boolean isAnnotated(Field field) {
|
||||
for (Class<? extends Annotation> annotation : requiredAnnotations) {
|
||||
for (Class<? extends Annotation> annotation : REQUIRED_ANNOTATIONS) {
|
||||
if (!field.isAnnotationPresent(annotation)) {
|
||||
return false;
|
||||
}
|
||||
@@ -206,11 +157,6 @@ public class BatchPropertyBeanPostProcessor implements BeanPostProcessor, BeanFa
|
||||
return !Modifier.isStatic(field.getModifiers()) && !Modifier.isFinal(field.getModifiers());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
|
||||
return bean;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||
if (!(beanFactory instanceof ConfigurableListableBeanFactory)) {
|
||||
|
||||
@@ -15,16 +15,17 @@
|
||||
*/
|
||||
package org.springframework.batch.core.jsr.configuration.support;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.regex.Pattern;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Simple context object to hold parsed JSR-352 batch properties, mapping properties
|
||||
* to beans / "batch artifacts". Used internally when parsing property tags from a batch
|
||||
* configuration file and to obtain corresponding values when injecting into batch artifacts.
|
||||
* Context object to hold parsed JSR-352 batch properties, mapping properties to beans /
|
||||
* "batch artifacts". Used internally when parsing property tags from a batch configuration
|
||||
* file and to obtain corresponding values when injecting into batch artifacts.
|
||||
* </p>
|
||||
*
|
||||
* @author Chris Schaefer
|
||||
@@ -32,139 +33,211 @@ import java.util.regex.Pattern;
|
||||
* @since 3.0
|
||||
*/
|
||||
public class BatchPropertyContext {
|
||||
private static final String JOB_ARTIFACT_PROPERTY_PREFIX = "job-";
|
||||
private static final Pattern JOB_PATH_DELIMITER_PATTERN = Pattern.compile("\\.");
|
||||
|
||||
private ConcurrentHashMap<String, Properties> batchProperties = new ConcurrentHashMap<String, Properties>();
|
||||
private Properties jobProperties = new Properties();
|
||||
private Map<String, Properties> stepProperties = new HashMap<String, Properties>();
|
||||
private Map<String, Properties> artifactProperties = new HashMap<String, Properties>();
|
||||
private Map<String, Map<String, Properties>> stepArtifactProperties = new HashMap<String, Map<String, Properties>>();
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Adds each of the provided {@link BatchPropertyContext} objects to the existing property
|
||||
* context.
|
||||
* Obtains the Job level properties.
|
||||
* </p>
|
||||
*
|
||||
* @return the Job level properties
|
||||
*/
|
||||
public Properties getJobProperties() {
|
||||
return jobProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Obtains the Step level properties for the provided Step name.
|
||||
* </p>
|
||||
*
|
||||
* @param stepName the Step name to obtain properties for
|
||||
* @return the {@link Properties} for the Step
|
||||
*/
|
||||
public Properties getStepProperties(String stepName) {
|
||||
Properties properties = stepProperties.get(stepName);
|
||||
|
||||
return properties != null ? properties : new Properties();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Obtains the batch {@link Properties} for the provided artifact name. The returned {@link Properties}
|
||||
* will also contain any job level properties that have been set. Job level properties will not override
|
||||
* existing lower level artifact properties.
|
||||
* </p>
|
||||
*
|
||||
* @param artifactName the batch artifact to obtain properties for
|
||||
* @return the {@link Properties} for the provided batch artifact
|
||||
*/
|
||||
public Properties getArtifactProperties(String artifactName) {
|
||||
Properties properties = new Properties();
|
||||
properties.putAll(getJobProperties());
|
||||
|
||||
if (artifactProperties.containsKey(artifactName)) {
|
||||
properties.putAll(artifactProperties.get(artifactName));
|
||||
}
|
||||
|
||||
return properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Obtains the batch {@link Properties} for the provided Step and artifact name.
|
||||
* </p>
|
||||
*
|
||||
* @param stepName the Step name the artifact is associated with
|
||||
* @param artifactName the artifact name to obtain {@link Properties} for
|
||||
* @return the {@link Properties} for the provided Step artifact
|
||||
*/
|
||||
public Properties getStepArtifactProperties(String stepName, String artifactName) {
|
||||
Properties properties = new Properties();
|
||||
properties.putAll(getStepProperties(stepName));
|
||||
|
||||
Map<String, Properties> artifactProperties = stepArtifactProperties.get(stepName);
|
||||
|
||||
if (artifactProperties != null && artifactProperties.containsKey(artifactName)) {
|
||||
properties.putAll(artifactProperties.get(artifactName));
|
||||
}
|
||||
|
||||
return properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Adds Job level properties to the context.
|
||||
* </p>
|
||||
*
|
||||
* @param batchPropertyContextEntries the {@link BatchPropertyContextEntry} objects to add
|
||||
*/
|
||||
public void setBatchContextEntries(List<BatchPropertyContextEntry> batchPropertyContextEntries) {
|
||||
for (BatchPropertyContextEntry batchPropertyContextEntry : batchPropertyContextEntries) {
|
||||
setBatchContextEntry(batchPropertyContextEntry);
|
||||
}
|
||||
}
|
||||
public void setJobPropertiesContextEntry(List<BatchPropertyContextEntry> batchPropertyContextEntries) {
|
||||
for(BatchPropertyContextEntry batchPropertyContextEntry : batchPropertyContextEntries) {
|
||||
Properties jobProperties = batchPropertyContextEntry.getProperties();
|
||||
|
||||
private void setBatchContextEntry(BatchPropertyContextEntry batchPropertyContextEntry) {
|
||||
String beanName = batchPropertyContextEntry.getBeanName();
|
||||
Properties properties = batchPropertyContextEntry.getProperties();
|
||||
|
||||
if (batchProperties.containsKey(beanName)) {
|
||||
Properties existingProperties = batchProperties.get(beanName);
|
||||
existingProperties.putAll(properties);
|
||||
|
||||
batchProperties.put(beanName, existingProperties);
|
||||
} else {
|
||||
batchProperties.put(beanName, properties);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Obtains all properties for the specific batch artifact / bean name without any job level
|
||||
* properties.
|
||||
* </p>
|
||||
*
|
||||
* @param beanName the batch artifact / bean name to obtain {@link Properties} for
|
||||
* @return the step level {@link Properties}
|
||||
*/
|
||||
public Properties getStepLevelProperties(String beanName) {
|
||||
Properties properties = new Properties();
|
||||
|
||||
if (batchProperties.containsKey(beanName)) {
|
||||
properties.putAll(batchProperties.get(beanName));
|
||||
}
|
||||
|
||||
return properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Obtains the batch {@link Properties} for the provided bean name / batch artifact. The returned
|
||||
* {@link Properties} will also contain any job level properties that have been set. Job level
|
||||
* properties will not override existing lower level artifact properties.
|
||||
* </p>
|
||||
*
|
||||
* @param beanName the bean name representing the batch artifact to obtain properties for
|
||||
* @return the {@link Properties} for the provided batch artifact
|
||||
*/
|
||||
public Properties getBatchProperties(String beanName) {
|
||||
Properties properties = new Properties();
|
||||
|
||||
if (batchProperties.containsKey(beanName)) {
|
||||
properties.putAll(batchProperties.get(beanName));
|
||||
}
|
||||
|
||||
Properties jobLevelProperties = getJobProperties();
|
||||
|
||||
for (String jobLevelProperty : jobLevelProperties.stringPropertyNames()) {
|
||||
if (!properties.containsKey(jobLevelProperty)) {
|
||||
properties.put(jobLevelProperty, jobLevelProperties.getProperty(jobLevelProperty));
|
||||
if (jobProperties != null && !jobProperties.isEmpty()) {
|
||||
this.jobProperties.putAll(jobProperties);
|
||||
}
|
||||
}
|
||||
|
||||
return properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Obtains all {@link Properties} that reside at the Job level configuration.
|
||||
* Adds Step level properties to the context.
|
||||
* </p>
|
||||
*
|
||||
* @return the job level {@link Properties}
|
||||
* @param batchPropertyContextEntries the {@link BatchPropertyContextEntry} objects to add
|
||||
*/
|
||||
public Properties getJobProperties() {
|
||||
Properties jobProperties = new Properties();
|
||||
public void setStepPropertiesContextEntry(List<BatchPropertyContextEntry> batchPropertyContextEntries) {
|
||||
for (BatchPropertyContextEntry batchPropertyContextEntry : batchPropertyContextEntries) {
|
||||
Assert.hasText(batchPropertyContextEntry.getArtifactName(), "Step name must be defined as the artifact name.");
|
||||
|
||||
for (String jobLevelProperty : batchProperties.keySet()) {
|
||||
if(isJobLevelComponentPath(jobLevelProperty)) {
|
||||
if (batchProperties.containsKey(jobLevelProperty)) {
|
||||
jobProperties.putAll(batchProperties.get(jobLevelProperty));
|
||||
break;
|
||||
String stepName = batchPropertyContextEntry.getArtifactName();
|
||||
Properties stepProperties = batchPropertyContextEntry.getProperties();
|
||||
|
||||
if (stepProperties != null && ! stepProperties.isEmpty()) {
|
||||
if (this.stepProperties.containsKey(stepName)) {
|
||||
Properties existingStepProperties = this.stepProperties.get(stepName);
|
||||
existingStepProperties.putAll(stepProperties);
|
||||
|
||||
this.stepProperties.put(stepName, existingStepProperties);
|
||||
} else {
|
||||
this.stepProperties.put(stepName, stepProperties);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return jobProperties;
|
||||
}
|
||||
|
||||
// for now we assume properties are using a path format, currently: jobName.componentName.artifactName
|
||||
// componentName can be the step name, job name prefixed by job- etc
|
||||
protected boolean isJobLevelComponentPath(String jobLevelProperty) {
|
||||
if(jobLevelProperty == null || "".equals(jobLevelProperty)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String[] path = JOB_PATH_DELIMITER_PATTERN.split(jobLevelProperty);
|
||||
|
||||
if (path.length >= 2) {
|
||||
String componentPath = path[1];
|
||||
|
||||
if (componentPath != null && componentPath.startsWith(JOB_ARTIFACT_PROPERTY_PREFIX)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Simple object to encapsulate batch properties of a given bean / batch artifact.
|
||||
* Adds non-Step scoped artifact properties to the context.
|
||||
* </p>
|
||||
*
|
||||
* @param batchPropertyContextEntries the {@link BatchPropertyContextEntry} objects to add
|
||||
*/
|
||||
public void setArtifactPropertiesContextEntry(List<BatchPropertyContextEntry> batchPropertyContextEntries) {
|
||||
for (BatchPropertyContextEntry batchPropertyContextEntry : batchPropertyContextEntries) {
|
||||
Assert.hasText(batchPropertyContextEntry.getArtifactName(), "Artifact name must be defined");
|
||||
|
||||
Properties properties = batchPropertyContextEntry.getProperties();
|
||||
String artifactName = batchPropertyContextEntry.getArtifactName();
|
||||
|
||||
if (properties != null && !properties.isEmpty()) {
|
||||
artifactProperties.put(artifactName, properties);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Adds Step scoped artifact properties to the context.
|
||||
* </p>
|
||||
*
|
||||
* @param batchPropertyContextEntries the {@link BatchPropertyContextEntry} objects to add
|
||||
*/
|
||||
public void setStepArtifactPropertiesContextEntry(List<BatchPropertyContextEntry> batchPropertyContextEntries) {
|
||||
for (BatchPropertyContextEntry batchPropertyContextEntry : batchPropertyContextEntries) {
|
||||
Assert.hasText(batchPropertyContextEntry.getStepName(), "Step name must be defined");
|
||||
Assert.hasText(batchPropertyContextEntry.getArtifactName(), "Artifact name must be defined");
|
||||
|
||||
String stepName = batchPropertyContextEntry.getStepName();
|
||||
final String artifactName = batchPropertyContextEntry.getArtifactName();
|
||||
|
||||
final Properties properties = batchPropertyContextEntry.getProperties();
|
||||
|
||||
if (!properties.isEmpty()) {
|
||||
Map<String, Properties> artifactProperties = stepArtifactProperties.get(stepName);
|
||||
|
||||
if (artifactProperties == null) {
|
||||
stepArtifactProperties.put(stepName, new HashMap<String, Properties>() {{
|
||||
put(artifactName, properties);
|
||||
}});
|
||||
} else {
|
||||
artifactProperties.put(artifactName, properties);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Obtains the property to be used when setting data for the provided {@link BatchArtifact.BatchArtifactType}.
|
||||
* </p>
|
||||
*
|
||||
* @param batchArtifactType the {@link BatchArtifact.BatchArtifactType} to lookup the property name for
|
||||
* @return the property name for
|
||||
* @throws IllegalStateException if an unhandled {@link BatchArtifact.BatchArtifactType} is encountered
|
||||
*/
|
||||
public String getPropertyName(BatchArtifact.BatchArtifactType batchArtifactType) {
|
||||
switch (batchArtifactType) {
|
||||
case STEP:
|
||||
return "stepPropertiesContextEntry";
|
||||
case STEP_ARTIFACT:
|
||||
return "stepArtifactPropertiesContextEntry";
|
||||
case ARTIFACT:
|
||||
return "artifactPropertiesContextEntry";
|
||||
case JOB:
|
||||
return "jobPropertiesContextEntry";
|
||||
default:
|
||||
throw new IllegalStateException("Unhandled BatchArtifactType of: " + batchArtifactType);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Simple object to encapsulate batch properties of a given batch artifact.
|
||||
* </p>
|
||||
*
|
||||
* @author Chris Schaefer
|
||||
* @since 3.0
|
||||
*/
|
||||
public class BatchPropertyContextEntry {
|
||||
private String beanName;
|
||||
private String stepName;
|
||||
private String artifactName;
|
||||
private Properties properties;
|
||||
private BatchArtifact.BatchArtifactType batchArtifactType;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
@@ -172,23 +245,25 @@ public class BatchPropertyContext {
|
||||
* and its associated {@link Properties}.
|
||||
* </p>
|
||||
*
|
||||
* @param beanName the bean name representing the batch artifact
|
||||
* @param artifactName the name representing the batch artifact
|
||||
* @param properties the associated {@link Properties}
|
||||
* @param batchArtifactType the associated {@link BatchArtifact.BatchArtifactType}
|
||||
*/
|
||||
public BatchPropertyContextEntry(String beanName, Properties properties) {
|
||||
this.beanName = beanName;
|
||||
this.properties = properties;
|
||||
public BatchPropertyContextEntry(String artifactName, Properties properties, BatchArtifact.BatchArtifactType batchArtifactType) {
|
||||
this.artifactName = artifactName;
|
||||
this.batchArtifactType = batchArtifactType;
|
||||
this.properties = properties != null ? properties : new Properties();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Obtains the bean name of the batch artifact this entry is associated with.
|
||||
* Obtains the name of the batch artifact this entry is associated with.
|
||||
* </p>
|
||||
*
|
||||
* @return the bean name of the batch artifact
|
||||
* @return the name of the batch artifact
|
||||
*/
|
||||
public String getBeanName() {
|
||||
return beanName;
|
||||
public String getArtifactName() {
|
||||
return artifactName;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -201,5 +276,38 @@ public class BatchPropertyContext {
|
||||
public Properties getProperties() {
|
||||
return properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Obtains the {@link BatchArtifact.BatchArtifactType} represented by this context entry.
|
||||
* </p>
|
||||
*
|
||||
* @return the {@link BatchArtifact.BatchArtifactType}
|
||||
*/
|
||||
public BatchArtifact.BatchArtifactType getBatchArtifactType() {
|
||||
return batchArtifactType;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Sets the Step name associated with this entry.
|
||||
* </p>
|
||||
*
|
||||
* @param stepName the Step name associated with this entry
|
||||
*/
|
||||
public void setStepName(String stepName) {
|
||||
this.stepName = stepName;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Obtains the Step name associated with this entry.
|
||||
* </p>
|
||||
*
|
||||
* @return the step name associated with this entry
|
||||
*/
|
||||
public String getStepName() {
|
||||
return stepName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* 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.Properties;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanDefinitionVisitor;
|
||||
import org.springframework.beans.factory.config.BeanExpressionContext;
|
||||
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.context.expression.StandardBeanExpressionResolver;
|
||||
import org.springframework.core.PriorityOrdered;
|
||||
import org.springframework.util.StringValueResolver;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* This post processor will resolve values in bean definitions that represent #{jobParameter['key']}
|
||||
* expressions prior to the standard SPeL resolution if they correspond with an entry in the user
|
||||
* provided {@link java.util.Properties} to the start or restart methods of the
|
||||
* {@link org.springframework.batch.core.jsr.launch.JsrJobOperator}. This allows jobProperty replacements
|
||||
* to occur for elements that require resolution prior to context initialization and are not step scoped.
|
||||
* </p>
|
||||
*
|
||||
* @author Chris Schaefer
|
||||
* @since 3.0
|
||||
*/
|
||||
public class JobParameterResolvingBeanFactoryPostProcessor implements BeanFactoryPostProcessor, PriorityOrdered {
|
||||
private Properties properties;
|
||||
private JobParameterResolver jobParameterResolver;
|
||||
private static final Pattern JOB_PARAMETERS_KEY_PATTERN = Pattern.compile("'([^']*?)'");
|
||||
private static final Pattern JOB_PARAMETERS_PATTERN = Pattern.compile("(#\\{jobParameters[^}]+\\})");
|
||||
|
||||
public JobParameterResolvingBeanFactoryPostProcessor(Properties properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.config.BeanFactoryPostProcessor#postProcessBeanFactory(org.springframework.beans.factory.config.ConfigurableListableBeanFactory)
|
||||
*/
|
||||
@Override
|
||||
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
|
||||
if (jobParameterResolver == null) {
|
||||
this.jobParameterResolver = new JobParameterResolver(beanFactory, properties);
|
||||
}
|
||||
|
||||
String[] beanNames = beanFactory.getBeanDefinitionNames();
|
||||
|
||||
for (String curName : beanNames) {
|
||||
jobParameterResolver.visitBeanDefinition(beanFactory.getBeanDefinition(curName));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets this {@link BeanFactoryPostProcessor} to the lowest precedence so that
|
||||
* it is executed as late as possible in the chain of {@link BeanFactoryPostProcessor}s
|
||||
*/
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return PriorityOrdered.LOWEST_PRECEDENCE;
|
||||
}
|
||||
|
||||
protected class JobParameterResolver {
|
||||
private Properties properties;
|
||||
private JsrExpressionParser jsrExpressionParser;
|
||||
private BeanDefinitionVisitor beanDefinitionVisitor;
|
||||
|
||||
public JobParameterResolver(ConfigurableListableBeanFactory beanFactory, Properties properties) {
|
||||
this.properties = properties;
|
||||
this.jsrExpressionParser = new JsrExpressionParser(new StandardBeanExpressionResolver(), new BeanExpressionContext(beanFactory, null));
|
||||
this.beanDefinitionVisitor = new BeanDefinitionVisitor(new JobParameterStringValueResolver());
|
||||
}
|
||||
|
||||
public void visitBeanDefinition(BeanDefinition beanDefinition) {
|
||||
if (properties != null && ! properties.isEmpty() && ! "step".equals(beanDefinition.getScope())) {
|
||||
beanDefinitionVisitor.visitBeanDefinition(beanDefinition);
|
||||
}
|
||||
}
|
||||
|
||||
protected class JobParameterStringValueResolver implements StringValueResolver {
|
||||
private static final String NULL = "null";
|
||||
|
||||
@Override
|
||||
public String resolveStringValue(String value) {
|
||||
if (value != null && ! "".equals(value)) {
|
||||
String resolvedString = resolveJobProperties(value);
|
||||
|
||||
if (!"".equals(resolvedString)) {
|
||||
return jsrExpressionParser.parseExpression(resolvedString);
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
private String resolveJobProperties(String value) {
|
||||
StringBuffer valueBuffer = new StringBuffer();
|
||||
Matcher jobParameterMatcher = JOB_PARAMETERS_PATTERN.matcher(value);
|
||||
|
||||
while (jobParameterMatcher.find()) {
|
||||
Matcher jobParameterKeyMatcher = JOB_PARAMETERS_KEY_PATTERN.matcher(jobParameterMatcher.group(1));
|
||||
|
||||
if (jobParameterKeyMatcher.find()) {
|
||||
String extractedProperty = jobParameterKeyMatcher.group(1);
|
||||
|
||||
if (properties.containsKey(extractedProperty)) {
|
||||
String resolvedProperty = properties.getProperty(extractedProperty);
|
||||
jobParameterMatcher.appendReplacement(valueBuffer, resolvedProperty);
|
||||
} else {
|
||||
jobParameterMatcher.appendReplacement(valueBuffer, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
jobParameterMatcher.appendTail(valueBuffer);
|
||||
|
||||
return valueBuffer.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,7 @@ 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.
|
||||
* example conditional/elvis statements need to be transformed a bit to be valid SPeL expressions.
|
||||
* </p>
|
||||
*
|
||||
* @author Chris Schaefer
|
||||
@@ -38,7 +38,7 @@ public class JsrExpressionParser {
|
||||
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 static final Pattern CONDITIONAL_EXPRESSION = Pattern.compile("(((\\bnull\\b)|(#\\{\\w))[^;]+)");
|
||||
|
||||
private BeanExpressionContext beanExpressionContext;
|
||||
private BeanExpressionResolver beanExpressionResolver;
|
||||
@@ -67,33 +67,37 @@ public class JsrExpressionParser {
|
||||
*/
|
||||
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 parseConditionalExpressions(expression);
|
||||
}
|
||||
|
||||
return (String) beanExpressionResolver.evaluate(expression, beanExpressionContext);
|
||||
}
|
||||
|
||||
private String parseConditionalExpressions(String expression) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,14 +15,10 @@
|
||||
*/
|
||||
package org.springframework.batch.core.jsr.configuration.support;
|
||||
|
||||
import java.util.Properties;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.PropertyValue;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanDefinitionVisitor;
|
||||
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
@@ -30,7 +26,6 @@ import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.core.PriorityOrdered;
|
||||
import org.springframework.util.StringValueResolver;
|
||||
|
||||
/**
|
||||
* After the {@link BeanFactory} is created, this post processor will evaluate to see
|
||||
@@ -38,26 +33,10 @@ import org.springframework.util.StringValueResolver;
|
||||
* to class names instead of bean names. If this is the case, a new {@link BeanDefinition}
|
||||
* is added with the name of the class as the bean name.
|
||||
*
|
||||
* This post processor will also resolve values in bean definitions that represent
|
||||
* #{jobParameter['key']} expressions prior to the standard SPeL resolution if they correspond
|
||||
* with an entry in the user provided {@link Properties} to the start or restart methods of the
|
||||
* {@link org.springframework.batch.core.jsr.launch.JsrJobOperator}. This allows jobProperty
|
||||
* replacements to occur for elements that require resolution prior to context initialization
|
||||
* and are not step scoped.
|
||||
*
|
||||
* @author Michael Minella
|
||||
* @author Chris Schaefer
|
||||
* @since 3.0
|
||||
*/
|
||||
public class ThreadLocalClassloaderBeanPostProcessor implements BeanFactoryPostProcessor, PriorityOrdered {
|
||||
private JobParameterResolver jobParameterResolver;
|
||||
private static final Pattern JOB_PARAMETERS_KEY_PATTERN = Pattern.compile("'([^']*?)'");
|
||||
private static final Pattern JOB_PARAMETERS_PATTERN = Pattern.compile("(#\\{jobParameters[^}]+\\})");
|
||||
|
||||
public ThreadLocalClassloaderBeanPostProcessor(Properties properties) {
|
||||
this.jobParameterResolver = new JobParameterResolver(properties);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.config.BeanFactoryPostProcessor#postProcessBeanFactory(org.springframework.beans.factory.config.ConfigurableListableBeanFactory)
|
||||
*/
|
||||
@@ -81,8 +60,6 @@ public class ThreadLocalClassloaderBeanPostProcessor implements BeanFactoryPostP
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
jobParameterResolver.visitBeanDefinition(beanDefinition);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,57 +71,4 @@ public class ThreadLocalClassloaderBeanPostProcessor implements BeanFactoryPostP
|
||||
public int getOrder() {
|
||||
return PriorityOrdered.LOWEST_PRECEDENCE;
|
||||
}
|
||||
|
||||
protected class JobParameterResolver {
|
||||
private Properties properties;
|
||||
private BeanDefinitionVisitor beanDefinitionVisitor;
|
||||
|
||||
public JobParameterResolver(Properties properties) {
|
||||
this.properties = properties;
|
||||
this.beanDefinitionVisitor = new BeanDefinitionVisitor(new JobParameterStringValueResolver());
|
||||
}
|
||||
|
||||
public void visitBeanDefinition(BeanDefinition beanDefinition) {
|
||||
if (properties != null && ! properties.isEmpty() && ! "step".equals(beanDefinition.getScope())) {
|
||||
beanDefinitionVisitor.visitBeanDefinition(beanDefinition);
|
||||
}
|
||||
}
|
||||
|
||||
protected class JobParameterStringValueResolver implements StringValueResolver {
|
||||
@Override
|
||||
public String resolveStringValue(String value) {
|
||||
if (value != null && ! "".equals(value)) {
|
||||
String resolvedString = resolveJobProperties(value);
|
||||
|
||||
if (!"".equals(resolvedString)) {
|
||||
return resolvedString;
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
private String resolveJobProperties(String value) {
|
||||
StringBuffer valueBuffer = new StringBuffer();
|
||||
Matcher jobParameterMatcher = JOB_PARAMETERS_PATTERN.matcher(value);
|
||||
|
||||
while (jobParameterMatcher.find()) {
|
||||
Matcher jobParameterKeyMatcher = JOB_PARAMETERS_KEY_PATTERN.matcher(jobParameterMatcher.group(1));
|
||||
|
||||
if (jobParameterKeyMatcher.find()) {
|
||||
String extractedProperty = jobParameterKeyMatcher.group(1);
|
||||
|
||||
if (properties.containsKey(extractedProperty)) {
|
||||
String resolvedProperty = properties.getProperty(extractedProperty);
|
||||
jobParameterMatcher.appendReplacement(valueBuffer, resolvedProperty);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
jobParameterMatcher.appendTail(valueBuffer);
|
||||
|
||||
return valueBuffer.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.springframework.batch.core.jsr.configuration.xml;
|
||||
|
||||
import org.springframework.batch.core.jsr.configuration.support.BatchArtifact;
|
||||
import org.springframework.batch.core.step.tasklet.Tasklet;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
@@ -36,7 +37,7 @@ import org.w3c.dom.Element;
|
||||
public class BatchletParser extends AbstractSingleBeanDefinitionParser {
|
||||
private static final String REF = "ref";
|
||||
|
||||
public void parseBatchlet(Element batchletElement, AbstractBeanDefinition bd, ParserContext parserContext) {
|
||||
public void parseBatchlet(Element batchletElement, AbstractBeanDefinition bd, ParserContext parserContext, String stepName) {
|
||||
bd.setBeanClass(StepFactoryBean.class);
|
||||
bd.setAttribute("isNamespaceStep", false);
|
||||
|
||||
@@ -49,6 +50,6 @@ public class BatchletParser extends AbstractSingleBeanDefinitionParser {
|
||||
bd.setRole(BeanDefinition.ROLE_SUPPORT);
|
||||
bd.setSource(parserContext.extractSource(batchletElement));
|
||||
|
||||
new PropertyParser(taskletRef, parserContext).parseProperties(batchletElement);
|
||||
new PropertyParser(taskletRef, parserContext, BatchArtifact.BatchArtifactType.STEP_ARTIFACT, stepName).parseProperties(batchletElement);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.springframework.batch.core.jsr.configuration.xml;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.batch.core.configuration.xml.ExceptionElementParser;
|
||||
import org.springframework.batch.core.jsr.configuration.support.BatchArtifact;
|
||||
import org.springframework.batch.core.step.item.ChunkOrientedTasklet;
|
||||
import org.springframework.batch.item.ItemProcessor;
|
||||
import org.springframework.batch.item.ItemReader;
|
||||
@@ -64,7 +65,7 @@ public class ChunkParser {
|
||||
private static final String ITEM_CHECKPOINT_POLICY = "item";
|
||||
private static final String CHECKPOINT_POLICY_ATTRIBUTE = "checkpoint-policy";
|
||||
|
||||
public void parse(Element element, AbstractBeanDefinition bd, ParserContext parserContext) {
|
||||
public void parse(Element element, AbstractBeanDefinition bd, ParserContext parserContext, String stepName) {
|
||||
MutablePropertyValues propertyValues = bd.getPropertyValues();
|
||||
bd.setBeanClass(StepFactoryBean.class);
|
||||
bd.setAttribute("isNamespaceStep", false);
|
||||
@@ -83,7 +84,7 @@ public class ChunkParser {
|
||||
|
||||
parseSimpleAttribute(element, propertyValues, TIME_LIMIT_ATTRIBUTE, "timeout");
|
||||
} else if(checkpointPolicy.equals(CUSTOM_CHECKPOINT_POLICY)) {
|
||||
parseCustomCheckpointAlgorithm(element, parserContext, propertyValues);
|
||||
parseCustomCheckpointAlgorithm(element, parserContext, propertyValues, stepName);
|
||||
}
|
||||
} else {
|
||||
String itemCount = element.getAttribute(ITEM_COUNT_ATTRIBUTE);
|
||||
@@ -103,7 +104,7 @@ public class ChunkParser {
|
||||
for (int i = 0; i < children.getLength(); i++) {
|
||||
Node nd = children.item(i);
|
||||
|
||||
parseChildElement(element, parserContext, propertyValues, nd);
|
||||
parseChildElement(element, parserContext, propertyValues, nd, stepName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,7 +118,7 @@ public class ChunkParser {
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
private void parseChildElement(Element element, ParserContext parserContext,
|
||||
MutablePropertyValues propertyValues, Node nd) {
|
||||
MutablePropertyValues propertyValues, Node nd, String stepName) {
|
||||
if (nd instanceof Element) {
|
||||
Element nestedElement = (Element) nd;
|
||||
String name = nestedElement.getLocalName();
|
||||
@@ -128,19 +129,19 @@ public class ChunkParser {
|
||||
propertyValues.addPropertyValue("itemReader", new RuntimeBeanReference(artifactName));
|
||||
}
|
||||
|
||||
new PropertyParser(artifactName, parserContext).parseProperties(nestedElement);
|
||||
new PropertyParser(artifactName, parserContext, BatchArtifact.BatchArtifactType.STEP_ARTIFACT, stepName).parseProperties(nestedElement);
|
||||
} else if(name.equals(PROCESSOR_ELEMENT)) {
|
||||
if (StringUtils.hasText(artifactName)) {
|
||||
propertyValues.addPropertyValue("itemProcessor", new RuntimeBeanReference(artifactName));
|
||||
}
|
||||
|
||||
new PropertyParser(artifactName, parserContext).parseProperties(nestedElement);
|
||||
new PropertyParser(artifactName, parserContext, BatchArtifact.BatchArtifactType.STEP_ARTIFACT, stepName).parseProperties(nestedElement);
|
||||
} else if(name.equals(WRITER_ELEMENT)) {
|
||||
if (StringUtils.hasText(artifactName)) {
|
||||
propertyValues.addPropertyValue("itemWriter", new RuntimeBeanReference(artifactName));
|
||||
}
|
||||
|
||||
new PropertyParser(artifactName, parserContext).parseProperties(nestedElement);
|
||||
new PropertyParser(artifactName, parserContext, BatchArtifact.BatchArtifactType.STEP_ARTIFACT, stepName).parseProperties(nestedElement);
|
||||
} else if(name.equals(SKIPPABLE_EXCEPTION_CLASSES_ELEMENT)) {
|
||||
ManagedMap exceptionClasses = new ExceptionElementParser().parse(element, parserContext, SKIPPABLE_EXCEPTION_CLASSES_ELEMENT);
|
||||
if(exceptionClasses != null) {
|
||||
@@ -165,7 +166,7 @@ public class ChunkParser {
|
||||
}
|
||||
}
|
||||
|
||||
private void parseCustomCheckpointAlgorithm(Element element, ParserContext parserContext, MutablePropertyValues propertyValues) {
|
||||
private void parseCustomCheckpointAlgorithm(Element element, ParserContext parserContext, MutablePropertyValues propertyValues, String stepName) {
|
||||
List<Element> elements = DomUtils.getChildElementsByTagName(element, CHECKPOINT_ALGORITHM_ELEMENT);
|
||||
|
||||
if(elements.size() == 1) {
|
||||
@@ -176,7 +177,7 @@ public class ChunkParser {
|
||||
propertyValues.addPropertyValue("chunkCompletionPolicy", new RuntimeBeanReference(name));
|
||||
}
|
||||
|
||||
new PropertyParser(name, parserContext).parseProperties(checkpointAlgorithmElement);
|
||||
new PropertyParser(name, parserContext, BatchArtifact.BatchArtifactType.STEP_ARTIFACT, stepName).parseProperties(checkpointAlgorithmElement);
|
||||
} else if(elements.size() > 1){
|
||||
parserContext.getReaderContext().error(
|
||||
"The <checkpoint-algorithm/> element may not appear more than once in a single <"
|
||||
|
||||
@@ -19,6 +19,7 @@ import java.util.Collection;
|
||||
|
||||
import org.springframework.batch.core.job.flow.JobExecutionDecider;
|
||||
import org.springframework.batch.core.job.flow.support.state.StepState;
|
||||
import org.springframework.batch.core.jsr.configuration.support.BatchArtifact;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
|
||||
@@ -50,8 +51,6 @@ public class DecisionParser {
|
||||
|
||||
String idAttribute = element.getAttribute(ID_ATTRIBUTE);
|
||||
|
||||
PropertyParser.pushPath(idAttribute);
|
||||
|
||||
parserContext.registerBeanComponent(new BeanComponentDefinition(factoryDefinition, idAttribute));
|
||||
stateBuilder.addConstructorArgReference(idAttribute);
|
||||
|
||||
@@ -63,12 +62,8 @@ public class DecisionParser {
|
||||
factoryDefinition.setAttribute("jobParserJobFactoryBeanRef", jobFactoryRef);
|
||||
}
|
||||
|
||||
new PropertyParser(refAttribute, parserContext).parseProperties(element);
|
||||
new PropertyParser(refAttribute, parserContext, BatchArtifact.BatchArtifactType.STEP_ARTIFACT, idAttribute).parseProperties(element);
|
||||
|
||||
Collection<BeanDefinition> nextElements = FlowParser.getNextElements(parserContext, stateBuilder.getBeanDefinition(), element);
|
||||
|
||||
PropertyParser.popPath();
|
||||
|
||||
return nextElements;
|
||||
return FlowParser.getNextElements(parserContext, stateBuilder.getBeanDefinition(), element);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ package org.springframework.batch.core.jsr.configuration.xml;
|
||||
|
||||
import org.springframework.batch.core.configuration.xml.CoreNamespaceUtils;
|
||||
import org.springframework.batch.core.jsr.StepContextFactoryBean;
|
||||
import org.springframework.batch.core.jsr.configuration.support.BatchArtifact;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
@@ -49,13 +50,6 @@ public class JobParser extends AbstractSingleBeanDefinitionParser {
|
||||
|
||||
String jobName = element.getAttribute(ID_ATTRIBUTE);
|
||||
|
||||
if(PropertyParser.hasPath()) {
|
||||
PropertyParser.clearPath();
|
||||
throw new IllegalArgumentException("Job parsing started for job name: " + jobName + " and property parser path already populated.");
|
||||
}
|
||||
|
||||
PropertyParser.pushPath(jobName);
|
||||
|
||||
builder.addConstructorArgValue(jobName);
|
||||
|
||||
String restartableAttribute = element.getAttribute(RESTARTABLE_ATTRIBUTE);
|
||||
@@ -63,6 +57,8 @@ public class JobParser extends AbstractSingleBeanDefinitionParser {
|
||||
builder.addPropertyValue("restartable", restartableAttribute);
|
||||
}
|
||||
|
||||
new PropertyParser(jobName, parserContext, BatchArtifact.BatchArtifactType.JOB).parseProperties(element);
|
||||
|
||||
BeanDefinition flowDef = new FlowParser(jobName, jobName).parse(element, parserContext);
|
||||
builder.addPropertyValue("flow", flowDef);
|
||||
|
||||
@@ -74,9 +70,5 @@ public class JobParser extends AbstractSingleBeanDefinitionParser {
|
||||
parserContext.getRegistry().registerBeanDefinition("stepContextFactory", stepContextBeanDefinition);
|
||||
|
||||
new ListnerParser(JobListenerFactoryBean.class, "jobExecutionListeners").parseListeners(element, parserContext, builder);
|
||||
|
||||
new PropertyParser(PropertyParser.JOB_ARTIFACT_PROPERTY_PREFIX + jobName, parserContext).parseProperties(element);
|
||||
|
||||
PropertyParser.popPath();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,8 +15,10 @@
|
||||
*/
|
||||
package org.springframework.batch.core.jsr.configuration.xml;
|
||||
|
||||
import java.util.HashMap;
|
||||
import org.springframework.batch.core.jsr.configuration.support.BatchPropertyBeanPostProcessor;
|
||||
import org.springframework.batch.core.jsr.configuration.support.JsrAutowiredAnnotationBeanPostProcessor;
|
||||
import org.springframework.batch.core.jsr.configuration.support.ThreadLocalClassloaderBeanPostProcessor;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
@@ -24,18 +26,22 @@ import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.context.annotation.AnnotationConfigUtils;
|
||||
|
||||
/**
|
||||
* Utility methods used in parsing of the JSR-352 batch namespace
|
||||
* Utility methods used in parsing of the JSR-352 batch namespace and related helpers.
|
||||
*
|
||||
* @author Michael Minella
|
||||
* @author Chris Schaefer
|
||||
* @since 3.0
|
||||
*/
|
||||
class JsrNamespaceUtils {
|
||||
private static final String JOB_PROPERTIES_BEAN_NAME = "jobProperties";
|
||||
private static final String BATCH_PROPERTY_POST_PROCESSOR_BEAN_NAME = "batchPropertyPostProcessor";
|
||||
private static final String THREAD_LOCAL_CLASS_LOADER_BEAN_POST_PROCESSOR_BEAN_NAME = "threadLocalClassloaderBeanPostProcessor";
|
||||
|
||||
static void autoregisterJsrBeansForNamespace(ParserContext parserContext) {
|
||||
autoRegisterJobProperties(parserContext);
|
||||
autoRegisterBatchPostProcessor(parserContext);
|
||||
autoRegisterJsrAutowiredAnnotationBeanPostProcessor(parserContext);
|
||||
autoRegisterThreadLocalClassloaderBeanPostProcessor(parserContext);
|
||||
}
|
||||
|
||||
private static void autoRegisterBatchPostProcessor(ParserContext parserContext) {
|
||||
@@ -46,6 +52,10 @@ class JsrNamespaceUtils {
|
||||
registerPostProcessor(parserContext, JsrAutowiredAnnotationBeanPostProcessor.class, BeanDefinition.ROLE_INFRASTRUCTURE, AnnotationConfigUtils.AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME);
|
||||
}
|
||||
|
||||
private static void autoRegisterThreadLocalClassloaderBeanPostProcessor(ParserContext parserContext) {
|
||||
registerPostProcessor(parserContext, ThreadLocalClassloaderBeanPostProcessor.class, BeanDefinition.ROLE_INFRASTRUCTURE, THREAD_LOCAL_CLASS_LOADER_BEAN_POST_PROCESSOR_BEAN_NAME);
|
||||
}
|
||||
|
||||
private static void registerPostProcessor(ParserContext parserContext, Class<?> clazz, int role, String beanName) {
|
||||
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(clazz);
|
||||
|
||||
@@ -54,4 +64,15 @@ class JsrNamespaceUtils {
|
||||
|
||||
parserContext.getRegistry().registerBeanDefinition(beanName, beanDefinition);
|
||||
}
|
||||
|
||||
// Registers a bean by the name of {@link #JOB_PROPERTIES_BEAN_NAME} so job level properties can be obtained through
|
||||
// for example a SPeL expression referencing #{jobProperties['key']} similar to systemProperties resolution.
|
||||
private static void autoRegisterJobProperties(ParserContext parserContext) {
|
||||
if (!parserContext.getRegistry().containsBeanDefinition(JOB_PROPERTIES_BEAN_NAME)) {
|
||||
AbstractBeanDefinition jobPropertiesBeanDefinition = BeanDefinitionBuilder.genericBeanDefinition(HashMap.class).getBeanDefinition();
|
||||
jobPropertiesBeanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
|
||||
parserContext.getRegistry().registerBeanDefinition(JOB_PROPERTIES_BEAN_NAME, jobPropertiesBeanDefinition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ package org.springframework.batch.core.jsr.configuration.xml;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.batch.core.jsr.configuration.support.BatchArtifact;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
import org.springframework.beans.factory.parsing.CompositeComponentDefinition;
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
@@ -48,8 +49,8 @@ public class ListnerParser {
|
||||
this.listenerType = listenerType;
|
||||
}
|
||||
|
||||
public void parseListeners(Element element, ParserContext parserContext, AbstractBeanDefinition bd) {
|
||||
ManagedList<AbstractBeanDefinition> listeners = parseListeners(element, parserContext);
|
||||
public void parseListeners(Element element, ParserContext parserContext, AbstractBeanDefinition bd, String stepName) {
|
||||
ManagedList<AbstractBeanDefinition> listeners = parseListeners(element, parserContext, stepName);
|
||||
|
||||
if(listeners.size() > 0) {
|
||||
bd.getPropertyValues().add(propertyKey, listeners);
|
||||
@@ -57,14 +58,14 @@ public class ListnerParser {
|
||||
}
|
||||
|
||||
public void parseListeners(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
|
||||
ManagedList<AbstractBeanDefinition> listeners = parseListeners(element, parserContext);
|
||||
ManagedList<AbstractBeanDefinition> listeners = parseListeners(element, parserContext, "");
|
||||
|
||||
if(listeners.size() > 0) {
|
||||
builder.addPropertyValue(propertyKey, listeners);
|
||||
}
|
||||
}
|
||||
|
||||
private ManagedList<AbstractBeanDefinition> parseListeners(Element element, ParserContext parserContext) {
|
||||
private ManagedList<AbstractBeanDefinition> parseListeners(Element element, ParserContext parserContext, String stepName) {
|
||||
List<Element> listenersElements = DomUtils.getChildElementsByTagName(element, LISTENERS_ELEMENT);
|
||||
|
||||
ManagedList<AbstractBeanDefinition> listeners = new ManagedList<AbstractBeanDefinition>();
|
||||
@@ -84,7 +85,7 @@ public class ListnerParser {
|
||||
|
||||
listeners.add(bd.getBeanDefinition());
|
||||
|
||||
new PropertyParser(beanName, parserContext).parseProperties(listenerElement);
|
||||
new PropertyParser(beanName, parserContext, getBatchArtifactType(stepName), stepName).parseProperties(listenerElement);
|
||||
}
|
||||
parserContext.popAndRegisterContainingComponent();
|
||||
}
|
||||
@@ -95,4 +96,9 @@ public class ListnerParser {
|
||||
|
||||
return listeners;
|
||||
}
|
||||
|
||||
private BatchArtifact.BatchArtifactType getBatchArtifactType(String stepName) {
|
||||
return (stepName != null && !"".equals(stepName)) ? BatchArtifact.BatchArtifactType.STEP_ARTIFACT
|
||||
: BatchArtifact.BatchArtifactType.ARTIFACT;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,73 +15,54 @@
|
||||
*/
|
||||
package org.springframework.batch.core.jsr.configuration.xml;
|
||||
|
||||
import java.util.Deque;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import org.springframework.batch.core.jsr.configuration.support.BatchArtifact;
|
||||
import org.springframework.batch.core.jsr.configuration.support.BatchPropertyContext;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.ManagedList;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.util.xml.DomUtils;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Parser for the <properties /> element defined by JSR-352. Parsed job level properties are
|
||||
* also registered as a Map similar to systemProperties. Job level properties can be obtained for
|
||||
* example through SPeL by referencing #{jobProperties['key']}.
|
||||
* Parser for the <properties /> element defined by JSR-352.
|
||||
* </p>
|
||||
*
|
||||
* @author Chris Schaefer
|
||||
* @since 3.0
|
||||
*/
|
||||
public class PropertyParser {
|
||||
static final String JOB_ARTIFACT_PROPERTY_PREFIX = "job-";
|
||||
private static final String PROPERTY_ELEMENT = "property";
|
||||
private static final String PROPERTIES_ELEMENT = "properties";
|
||||
private static final String PROPERTY_NAME_ATTRIBUTE = "name";
|
||||
private static final String PROPERTY_VALUE_ATTRIBUTE = "value";
|
||||
private static final String JOB_PROPERTIES_BEAN_NAME = "jobProperties";
|
||||
private static final String BATCH_CONTEXT_ENTRIES_PROPERTY_NAME = "batchContextEntries";
|
||||
private static final String BATCH_PROPERTY_CONTEXT_BEAN_CLASS_NAME = "org.springframework.batch.core.jsr.configuration.support.BatchPropertyContext";
|
||||
private static final String BATCH_PROPERTY_CONTEXT_BEAN_NAME = "batchPropertyContext";
|
||||
private static final Deque<String> PATH = new LinkedList<String>();
|
||||
|
||||
private String beanName;
|
||||
private String stepName;
|
||||
private ParserContext parserContext;
|
||||
private BatchArtifact.BatchArtifactType batchArtifactType;
|
||||
|
||||
public PropertyParser(String beanName, ParserContext parserContext) {
|
||||
public PropertyParser(String beanName, ParserContext parserContext, BatchArtifact.BatchArtifactType batchArtifactType) {
|
||||
this.beanName = beanName;
|
||||
this.parserContext = parserContext;
|
||||
this.batchArtifactType = batchArtifactType;
|
||||
|
||||
registerBatchPropertyContext();
|
||||
registerJobProperties();
|
||||
}
|
||||
|
||||
public PropertyParser(ParserContext parserContext) {
|
||||
this("", parserContext);
|
||||
}
|
||||
|
||||
public static void pushPath(String pathElement) {
|
||||
PATH.push(pathElement);
|
||||
}
|
||||
|
||||
public static void popPath() {
|
||||
PATH.pop();
|
||||
}
|
||||
|
||||
public static boolean hasPath() {
|
||||
return !PATH.isEmpty();
|
||||
}
|
||||
|
||||
public static void clearPath() {
|
||||
PATH.clear();
|
||||
public PropertyParser(String beanName, ParserContext parserContext, BatchArtifact.BatchArtifactType batchArtifactType, String stepName) {
|
||||
this(beanName, parserContext, batchArtifactType);
|
||||
this.stepName = stepName;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -118,13 +99,17 @@ public class PropertyParser {
|
||||
|
||||
BatchPropertyContext batchPropertyContext = new BatchPropertyContext();
|
||||
BatchPropertyContext.BatchPropertyContextEntry batchPropertyContextEntry =
|
||||
batchPropertyContext.new BatchPropertyContextEntry(getContextEntryKey(), properties);
|
||||
batchPropertyContext.new BatchPropertyContextEntry(beanName, properties, batchArtifactType);
|
||||
|
||||
if (StringUtils.hasText(stepName)) {
|
||||
batchPropertyContextEntry.setStepName(stepName);
|
||||
}
|
||||
|
||||
ManagedList<BatchPropertyContext.BatchPropertyContextEntry> managedList = new ManagedList<BatchPropertyContext.BatchPropertyContextEntry>();
|
||||
managedList.setMergeEnabled(true);
|
||||
managedList.add(batchPropertyContextEntry);
|
||||
|
||||
beanDefinition.getPropertyValues().addPropertyValue(BATCH_CONTEXT_ENTRIES_PROPERTY_NAME, managedList);
|
||||
beanDefinition.getPropertyValues().addPropertyValue(batchPropertyContext.getPropertyName(batchArtifactType), managedList);
|
||||
}
|
||||
|
||||
private void registerBatchPropertyContext() {
|
||||
@@ -139,20 +124,25 @@ public class PropertyParser {
|
||||
}
|
||||
}
|
||||
|
||||
private void registerJobProperties() {
|
||||
if (!parserContext.getRegistry().containsBeanDefinition(JOB_PROPERTIES_BEAN_NAME)) {
|
||||
AbstractBeanDefinition jobPropertiesBeanDefinition = BeanDefinitionBuilder.genericBeanDefinition(HashMap.class).getBeanDefinition();
|
||||
jobPropertiesBeanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
private void setJobProperties(Properties properties) {
|
||||
if (batchArtifactType.equals(BatchArtifact.BatchArtifactType.JOB)) {
|
||||
BeanDefinition beanDefinition = parserContext.getRegistry().getBeanDefinition(BATCH_PROPERTY_CONTEXT_BEAN_NAME);
|
||||
|
||||
parserContext.getRegistry().registerBeanDefinition(JOB_PROPERTIES_BEAN_NAME, jobPropertiesBeanDefinition);
|
||||
BatchPropertyContext batchPropertyContext = new BatchPropertyContext();
|
||||
BatchPropertyContext.BatchPropertyContextEntry batchPropertyContextEntry =
|
||||
batchPropertyContext.new BatchPropertyContextEntry(beanName, properties, batchArtifactType);
|
||||
|
||||
beanDefinition.getPropertyValues().addPropertyValue(batchPropertyContext.getPropertyName(batchArtifactType), batchPropertyContextEntry);
|
||||
|
||||
registerJobProperties(properties);
|
||||
}
|
||||
}
|
||||
|
||||
private void setJobProperties(Properties properties) {
|
||||
if (beanName.startsWith(JOB_ARTIFACT_PROPERTY_PREFIX)) {
|
||||
private void registerJobProperties(Properties properties) {
|
||||
if (batchArtifactType.equals(BatchArtifact.BatchArtifactType.JOB)) {
|
||||
Map<String, String> jobProperties = new HashMap<String, String>();
|
||||
|
||||
if (properties != null) {
|
||||
if (properties != null && ! properties.isEmpty()) {
|
||||
for (String param : properties.stringPropertyNames()) {
|
||||
jobProperties.put(param, properties.getProperty(param));
|
||||
}
|
||||
@@ -162,23 +152,4 @@ public class PropertyParser {
|
||||
jobPropertiesBeanDefinition.getConstructorArgumentValues().addGenericArgumentValue(jobProperties);
|
||||
}
|
||||
}
|
||||
|
||||
private String getPath() {
|
||||
StringBuilder pathBuilder = new StringBuilder();
|
||||
Iterator pathIterator = PATH.descendingIterator();
|
||||
|
||||
if (pathIterator.hasNext()) {
|
||||
pathBuilder.append(pathIterator.next());
|
||||
|
||||
while (pathIterator.hasNext()) {
|
||||
pathBuilder.append(".").append(pathIterator.next());
|
||||
}
|
||||
}
|
||||
|
||||
return pathBuilder.toString();
|
||||
}
|
||||
|
||||
private String getContextEntryKey() {
|
||||
return "".equals(beanName) ? getPath() : getPath() + "." + beanName;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.springframework.batch.core.jsr.configuration.xml;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.batch.core.job.flow.support.state.StepState;
|
||||
import org.springframework.batch.core.jsr.configuration.support.BatchArtifact;
|
||||
import org.springframework.batch.core.listener.StepListenerFactoryBean;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
|
||||
@@ -55,8 +56,6 @@ public class StepParser extends AbstractSingleBeanDefinitionParser {
|
||||
String stepName = element.getAttribute(SPLIT_ID_ATTRIBUTE);
|
||||
builder.addPropertyValue("name", stepName);
|
||||
|
||||
PropertyParser.pushPath(stepName);
|
||||
|
||||
parserContext.registerBeanComponent(new BeanComponentDefinition(bd, stepName));
|
||||
stateBuilder.addConstructorArgReference(stepName);
|
||||
|
||||
@@ -71,8 +70,8 @@ public class StepParser extends AbstractSingleBeanDefinitionParser {
|
||||
allowStartIfComplete);
|
||||
}
|
||||
|
||||
new ListnerParser(StepListenerFactoryBean.class, "listeners").parseListeners(element, parserContext, bd);
|
||||
new PropertyParser(parserContext).parseProperties(element);
|
||||
new ListnerParser(StepListenerFactoryBean.class, "listeners").parseListeners(element, parserContext, bd, stepName);
|
||||
new PropertyParser(stepName, parserContext, BatchArtifact.BatchArtifactType.STEP).parseProperties(element);
|
||||
|
||||
// look at all nested elements
|
||||
NodeList children = element.getChildNodes();
|
||||
@@ -85,17 +84,15 @@ public class StepParser extends AbstractSingleBeanDefinitionParser {
|
||||
String name = nestedElement.getLocalName();
|
||||
|
||||
if(name.equalsIgnoreCase(BATCHLET_ELEMENT)) {
|
||||
new BatchletParser().parseBatchlet(nestedElement, bd, parserContext);
|
||||
new BatchletParser().parseBatchlet(nestedElement, bd, parserContext, stepName);
|
||||
} else if(name.equals(CHUNK_ELEMENT)) {
|
||||
new ChunkParser().parse(nestedElement, bd, parserContext);
|
||||
new ChunkParser().parse(nestedElement, bd, parserContext, stepName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Collection<BeanDefinition> nextElements = FlowParser.getNextElements(parserContext, stepName, stateBuilder.getBeanDefinition(), element);
|
||||
|
||||
PropertyParser.popPath();
|
||||
|
||||
return nextElements;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ import org.springframework.batch.core.explore.JobExplorer;
|
||||
import org.springframework.batch.core.jsr.JobContext;
|
||||
import org.springframework.batch.core.jsr.JsrJobParametersConverter;
|
||||
import org.springframework.batch.core.jsr.configuration.support.BatchPropertyContext;
|
||||
import org.springframework.batch.core.jsr.configuration.support.ThreadLocalClassloaderBeanPostProcessor;
|
||||
import org.springframework.batch.core.jsr.configuration.support.JobParameterResolvingBeanFactoryPostProcessor;
|
||||
import org.springframework.batch.core.repository.JobRepository;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.access.BeanFactoryLocator;
|
||||
@@ -452,7 +452,7 @@ public class JsrJobOperator implements JobOperator, InitializingBean {
|
||||
batchContext.load(jobXml);
|
||||
}
|
||||
|
||||
batchContext.addBeanFactoryPostProcessor(new ThreadLocalClassloaderBeanPostProcessor(params));
|
||||
batchContext.addBeanFactoryPostProcessor(new JobParameterResolvingBeanFactoryPostProcessor(params));
|
||||
batchContext.setParent(baseContext);
|
||||
batchContext.refresh();
|
||||
|
||||
@@ -476,7 +476,7 @@ public class JsrJobOperator implements JobOperator, InitializingBean {
|
||||
ConfigurableListableBeanFactory factory = batchContext.getBeanFactory();
|
||||
|
||||
BatchPropertyContext batchPropertyContext = factory.getBean(BATCH_PROPERTY_CONTEXT_BEAN_NAME, BatchPropertyContext.class);
|
||||
Properties properties = batchPropertyContext.getBatchProperties("job-" + job.getName());
|
||||
Properties properties = batchPropertyContext.getJobProperties();
|
||||
|
||||
factory.registerSingleton(job.getName() + "_" + jobExecution.getId() + "_jobContext", new JobContext(jobExecution, properties));
|
||||
|
||||
@@ -553,7 +553,7 @@ public class JsrJobOperator implements JobOperator, InitializingBean {
|
||||
batchContext.load(jobXml);
|
||||
}
|
||||
|
||||
batchContext.addBeanFactoryPostProcessor(new ThreadLocalClassloaderBeanPostProcessor(params));
|
||||
batchContext.addBeanFactoryPostProcessor(new JobParameterResolvingBeanFactoryPostProcessor(params));
|
||||
batchContext.setParent(baseContext);
|
||||
batchContext.refresh();
|
||||
|
||||
@@ -575,7 +575,7 @@ public class JsrJobOperator implements JobOperator, InitializingBean {
|
||||
ConfigurableListableBeanFactory factory = batchContext.getBeanFactory();
|
||||
|
||||
BatchPropertyContext batchPropertyContext = factory.getBean(BATCH_PROPERTY_CONTEXT_BEAN_NAME, BatchPropertyContext.class);
|
||||
Properties properties = batchPropertyContext.getBatchProperties("job-" + job.getName());
|
||||
Properties properties = batchPropertyContext.getJobProperties();
|
||||
|
||||
factory.registerSingleton(job.getName() + "_" + jobExecution.getId() + "_jobContext", new JobContext(jobExecution, properties));
|
||||
|
||||
|
||||
@@ -16,13 +16,12 @@
|
||||
package org.springframework.batch.core.jsr.configuration.support;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
@@ -32,7 +31,10 @@ import static org.junit.Assert.assertTrue;
|
||||
* @author Chris Schaefer
|
||||
*/
|
||||
public class BatchPropertyContextTests {
|
||||
private List<BatchPropertyContext.BatchPropertyContextEntry> entries = new ArrayList<BatchPropertyContext.BatchPropertyContextEntry>();
|
||||
private List<BatchPropertyContext.BatchPropertyContextEntry> jobProperties = new ArrayList<BatchPropertyContext.BatchPropertyContextEntry>();
|
||||
private List<BatchPropertyContext.BatchPropertyContextEntry> stepProperties = new ArrayList<BatchPropertyContext.BatchPropertyContextEntry>();
|
||||
private List<BatchPropertyContext.BatchPropertyContextEntry> artifactProperties = new ArrayList<BatchPropertyContext.BatchPropertyContextEntry>();
|
||||
private List<BatchPropertyContext.BatchPropertyContextEntry> stepArtifactProperties = new ArrayList<BatchPropertyContext.BatchPropertyContextEntry>();
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
@@ -41,83 +43,55 @@ public class BatchPropertyContextTests {
|
||||
Properties step1Properties = new Properties();
|
||||
step1Properties.setProperty("step1PropertyName1", "step1PropertyValue1");
|
||||
step1Properties.setProperty("step1PropertyName2", "step1PropertyValue2");
|
||||
entries.add(batchPropertyContext.new BatchPropertyContextEntry("job1.step1", step1Properties));
|
||||
stepProperties.add(batchPropertyContext.new BatchPropertyContextEntry("step1", step1Properties, BatchArtifact.BatchArtifactType.STEP));
|
||||
|
||||
Properties step2Properties = new Properties();
|
||||
step2Properties.setProperty("step2PropertyName1", "step2PropertyValue1");
|
||||
step2Properties.setProperty("step2PropertyName2", "step2PropertyValue2");
|
||||
entries.add(batchPropertyContext.new BatchPropertyContextEntry("job1.step2", step2Properties));
|
||||
stepProperties.add(batchPropertyContext.new BatchPropertyContextEntry("step2", step2Properties, BatchArtifact.BatchArtifactType.STEP));
|
||||
|
||||
Properties jobProperties = new Properties();
|
||||
jobProperties.setProperty("jobProperty1", "jobProperty1value");
|
||||
jobProperties.setProperty("jobProperty2", "jobProperty2value");
|
||||
entries.add(batchPropertyContext.new BatchPropertyContextEntry("job1.job-job1", jobProperties));
|
||||
this.jobProperties.add(batchPropertyContext.new BatchPropertyContextEntry("job1", jobProperties, BatchArtifact.BatchArtifactType.JOB));
|
||||
|
||||
Properties artifactProperties = new Properties();
|
||||
artifactProperties.setProperty("deciderProperty1", "deciderProperty1value");
|
||||
artifactProperties.setProperty("deciderProperty2", "deciderProperty2value");
|
||||
this.artifactProperties.add(batchPropertyContext.new BatchPropertyContextEntry("decider1", artifactProperties, BatchArtifact.BatchArtifactType.ARTIFACT));
|
||||
|
||||
Properties stepArtifactProperties = new Properties();
|
||||
stepArtifactProperties.setProperty("readerProperty1", "readerProperty1value");
|
||||
stepArtifactProperties.setProperty("readerProperty2", "readerProperty2value");
|
||||
|
||||
BatchPropertyContext.BatchPropertyContextEntry batchPropertyContextEntry =
|
||||
batchPropertyContext.new BatchPropertyContextEntry("reader", stepArtifactProperties, BatchArtifact.BatchArtifactType.STEP_ARTIFACT);
|
||||
batchPropertyContextEntry.setStepName("step1");
|
||||
|
||||
this.stepArtifactProperties.add(batchPropertyContextEntry);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddBatchContextEntries() {
|
||||
public void testStepLevelProperties() {
|
||||
BatchPropertyContext batchPropertyContext = new BatchPropertyContext();
|
||||
batchPropertyContext.setBatchContextEntries(entries);
|
||||
batchPropertyContext.setJobPropertiesContextEntry(jobProperties);
|
||||
batchPropertyContext.setStepPropertiesContextEntry(stepProperties);
|
||||
|
||||
Properties step1BatchProperties = batchPropertyContext.getBatchProperties("job1.step1");
|
||||
assertEquals(4, step1BatchProperties.size());
|
||||
assertEquals("step1PropertyValue1", step1BatchProperties.getProperty("step1PropertyName1"));
|
||||
assertEquals("step1PropertyValue2", step1BatchProperties.getProperty("step1PropertyName2"));
|
||||
assertEquals("jobProperty1value", step1BatchProperties.getProperty("jobProperty1"));
|
||||
assertEquals("jobProperty2value", step1BatchProperties.getProperty("jobProperty2"));
|
||||
Properties step1Properties = batchPropertyContext.getStepProperties("step1");
|
||||
assertEquals(2, step1Properties.size());
|
||||
assertEquals("step1PropertyValue1", step1Properties.getProperty("step1PropertyName1"));
|
||||
assertEquals("step1PropertyValue2", step1Properties.getProperty("step1PropertyName2"));
|
||||
|
||||
Properties step2BatchProperties = batchPropertyContext.getBatchProperties("job1.step2");
|
||||
assertEquals(4, step2BatchProperties.size());
|
||||
assertEquals("step2PropertyValue1", step2BatchProperties.getProperty("step2PropertyName1"));
|
||||
assertEquals("step2PropertyValue2", step2BatchProperties.getProperty("step2PropertyName2"));
|
||||
assertEquals("jobProperty1value", step2BatchProperties.getProperty("jobProperty1"));
|
||||
assertEquals("jobProperty2value", step2BatchProperties.getProperty("jobProperty2"));
|
||||
|
||||
Properties jobProperties = batchPropertyContext.getBatchProperties("job1.job-job1");
|
||||
assertEquals(2, jobProperties.size());
|
||||
assertEquals("jobProperty1value", jobProperties.getProperty("jobProperty1"));
|
||||
assertEquals("jobProperty2value", jobProperties.getProperty("jobProperty2"));
|
||||
Properties step2Properties = batchPropertyContext.getStepProperties("step2");
|
||||
assertEquals(2, step2Properties.size());
|
||||
assertEquals("step2PropertyValue1", step2Properties.getProperty("step2PropertyName1"));
|
||||
assertEquals("step2PropertyValue2", step2Properties.getProperty("step2PropertyName2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddBatchContextEntriesToExistingArtifact() {
|
||||
public void testJobLevelProperties() {
|
||||
BatchPropertyContext batchPropertyContext = new BatchPropertyContext();
|
||||
|
||||
Properties step1properties = new Properties();
|
||||
step1properties.setProperty("newStep1PropertyName", "newStep1PropertyValue");
|
||||
entries.add(batchPropertyContext.new BatchPropertyContextEntry("job1.step1", step1properties));
|
||||
|
||||
batchPropertyContext.setBatchContextEntries(entries);
|
||||
|
||||
Properties bean2 = batchPropertyContext.getBatchProperties("job1.step1");
|
||||
assertEquals(5, bean2.size());
|
||||
assertEquals("step1PropertyValue1", bean2.getProperty("step1PropertyName1"));
|
||||
assertEquals("step1PropertyValue2", bean2.getProperty("step1PropertyName2"));
|
||||
assertEquals("newStep1PropertyValue", bean2.getProperty("newStep1PropertyName"));
|
||||
assertEquals("jobProperty1value", bean2.getProperty("jobProperty1"));
|
||||
assertEquals("jobProperty2value", bean2.getProperty("jobProperty2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetStepLevelProperties() {
|
||||
BatchPropertyContext batchPropertyContext = new BatchPropertyContext();
|
||||
batchPropertyContext.setBatchContextEntries(entries);
|
||||
|
||||
Properties bean1 = batchPropertyContext.getStepLevelProperties("job1.step1");
|
||||
assertEquals(2, bean1.size());
|
||||
assertEquals("step1PropertyValue1", bean1.getProperty("step1PropertyName1"));
|
||||
assertEquals("step1PropertyValue2", bean1.getProperty("step1PropertyName2"));
|
||||
|
||||
Properties bean2 = batchPropertyContext.getStepLevelProperties("job1.step2");
|
||||
assertEquals(2, bean2.size());
|
||||
assertEquals("step2PropertyValue1", bean2.getProperty("step2PropertyName1"));
|
||||
assertEquals("step2PropertyValue2", bean2.getProperty("step2PropertyName2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJobProperties() {
|
||||
BatchPropertyContext batchPropertyContext = new BatchPropertyContext();
|
||||
batchPropertyContext.setBatchContextEntries(entries);
|
||||
batchPropertyContext.setJobPropertiesContextEntry(jobProperties);
|
||||
|
||||
Properties jobProperties = batchPropertyContext.getJobProperties();
|
||||
assertEquals(2, jobProperties.size());
|
||||
@@ -126,56 +100,83 @@ public class BatchPropertyContextTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJobNonOverridingJobProperties() {
|
||||
public void testAddPropertiesToExistingStep() {
|
||||
BatchPropertyContext batchPropertyContext = new BatchPropertyContext();
|
||||
batchPropertyContext.setJobPropertiesContextEntry(jobProperties);
|
||||
batchPropertyContext.setStepPropertiesContextEntry(stepProperties);
|
||||
|
||||
Properties jobProperties = new Properties();
|
||||
jobProperties.setProperty("step1PropertyName1", "step1PropertyOverride");
|
||||
entries.add(batchPropertyContext.new BatchPropertyContextEntry("job1.job-job1", jobProperties));
|
||||
Properties step1 = batchPropertyContext.getStepProperties("step1");
|
||||
assertEquals(2, step1.size());
|
||||
assertEquals("step1PropertyValue1", step1.getProperty("step1PropertyName1"));
|
||||
assertEquals("step1PropertyValue2", step1.getProperty("step1PropertyName2"));
|
||||
|
||||
batchPropertyContext.setBatchContextEntries(entries);
|
||||
Properties step1properties = new Properties();
|
||||
step1properties.setProperty("newStep1PropertyName", "newStep1PropertyValue");
|
||||
|
||||
Properties bean1 = batchPropertyContext.getBatchProperties("job1.step1");
|
||||
assertEquals(4, bean1.size());
|
||||
assertEquals("step1PropertyValue1", bean1.getProperty("step1PropertyName1"));
|
||||
assertEquals("step1PropertyValue2", bean1.getProperty("step1PropertyName2"));
|
||||
assertEquals("jobProperty1value", bean1.getProperty("jobProperty1"));
|
||||
assertEquals("jobProperty2value", bean1.getProperty("jobProperty2"));
|
||||
batchPropertyContext.setStepPropertiesContextEntry(
|
||||
Collections.singletonList(batchPropertyContext.new BatchPropertyContextEntry("step1", step1properties, BatchArtifact.BatchArtifactType.STEP)));
|
||||
|
||||
Properties testJobBean = batchPropertyContext.getBatchProperties("job1.job-job1");
|
||||
assertEquals(3, testJobBean.size());
|
||||
assertEquals("step1PropertyOverride", testJobBean.getProperty("step1PropertyName1"));
|
||||
assertEquals("jobProperty1value", testJobBean.getProperty("jobProperty1"));
|
||||
assertEquals("jobProperty2value", testJobBean.getProperty("jobProperty2"));
|
||||
Properties step1updated = batchPropertyContext.getStepProperties("step1");
|
||||
assertEquals(3, step1updated.size());
|
||||
assertEquals("step1PropertyValue1", step1updated.getProperty("step1PropertyName1"));
|
||||
assertEquals("step1PropertyValue2", step1updated.getProperty("step1PropertyName2"));
|
||||
assertEquals("newStep1PropertyValue", step1updated.getProperty("newStep1PropertyName"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJobLevelPropertiesWithPath() {
|
||||
List<BatchPropertyContext.BatchPropertyContextEntry> entries = new ArrayList<BatchPropertyContext.BatchPropertyContextEntry>();
|
||||
|
||||
public void testNonStepLevelArtifactProperties() {
|
||||
BatchPropertyContext batchPropertyContext = new BatchPropertyContext();
|
||||
batchPropertyContext.setJobPropertiesContextEntry(jobProperties);
|
||||
batchPropertyContext.setArtifactPropertiesContextEntry(artifactProperties);
|
||||
batchPropertyContext.setStepPropertiesContextEntry(stepProperties);
|
||||
|
||||
Properties jobProperties = new Properties();
|
||||
jobProperties.setProperty("readerName", "testJobreaderName");
|
||||
|
||||
entries.add(batchPropertyContext.new BatchPropertyContextEntry("job1.job-job1.itemReader", jobProperties));
|
||||
|
||||
batchPropertyContext.setBatchContextEntries(entries);
|
||||
|
||||
Properties props = batchPropertyContext.getJobProperties();
|
||||
assertEquals(1, props.size());
|
||||
assertEquals("testJobreaderName", props.getProperty("readerName"));
|
||||
Properties artifactProperties = batchPropertyContext.getArtifactProperties("decider1");
|
||||
assertEquals(4, artifactProperties.size());
|
||||
assertEquals("deciderProperty1value", artifactProperties.getProperty("deciderProperty1"));
|
||||
assertEquals("deciderProperty2value", artifactProperties.getProperty("deciderProperty2"));
|
||||
assertEquals("jobProperty1value", artifactProperties.getProperty("jobProperty1"));
|
||||
assertEquals("jobProperty2value", artifactProperties.getProperty("jobProperty2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJobLevelComponentPath() {
|
||||
public void testStepLevelArtifactProperties() {
|
||||
BatchPropertyContext batchPropertyContext = new BatchPropertyContext();
|
||||
assertTrue(batchPropertyContext.isJobLevelComponentPath("myJob.job-myJob"));
|
||||
assertTrue(batchPropertyContext.isJobLevelComponentPath("myJob.job-myJob.myReader"));
|
||||
assertTrue(batchPropertyContext.isJobLevelComponentPath("myJob.job-myJob.myReader.something"));
|
||||
assertFalse(batchPropertyContext.isJobLevelComponentPath("myJob"));
|
||||
assertFalse(batchPropertyContext.isJobLevelComponentPath("job-myJob"));
|
||||
assertFalse(batchPropertyContext.isJobLevelComponentPath(null));
|
||||
assertFalse(batchPropertyContext.isJobLevelComponentPath("myJob."));
|
||||
batchPropertyContext.setJobPropertiesContextEntry(jobProperties);
|
||||
batchPropertyContext.setArtifactPropertiesContextEntry(artifactProperties);
|
||||
batchPropertyContext.setStepPropertiesContextEntry(stepProperties);
|
||||
batchPropertyContext.setStepArtifactPropertiesContextEntry(stepArtifactProperties);
|
||||
|
||||
Properties artifactProperties = batchPropertyContext.getStepArtifactProperties("step1", "reader");
|
||||
assertEquals(4, artifactProperties.size());
|
||||
assertEquals("readerProperty1value", artifactProperties.getProperty("readerProperty1"));
|
||||
assertEquals("readerProperty2value", artifactProperties.getProperty("readerProperty2"));
|
||||
assertEquals("step1PropertyValue1", artifactProperties.getProperty("step1PropertyName1"));
|
||||
assertEquals("step1PropertyValue2", artifactProperties.getProperty("step1PropertyName2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testArtifactNonOverridingJobProperties() {
|
||||
BatchPropertyContext batchPropertyContext = new BatchPropertyContext();
|
||||
batchPropertyContext.setJobPropertiesContextEntry(jobProperties);
|
||||
batchPropertyContext.setArtifactPropertiesContextEntry(artifactProperties);
|
||||
|
||||
Properties jobProperties = new Properties();
|
||||
jobProperties.setProperty("deciderProperty1", "decider1PropertyOverride");
|
||||
|
||||
batchPropertyContext.setJobPropertiesContextEntry(
|
||||
Collections.singletonList(batchPropertyContext.new BatchPropertyContextEntry("job1", jobProperties, BatchArtifact.BatchArtifactType.JOB)));
|
||||
|
||||
Properties step1 = batchPropertyContext.getArtifactProperties("decider1");
|
||||
assertEquals(4, step1.size());
|
||||
assertEquals("deciderProperty1value", step1.getProperty("deciderProperty1"));
|
||||
assertEquals("deciderProperty2value", step1.getProperty("deciderProperty2"));
|
||||
assertEquals("jobProperty1value", step1.getProperty("jobProperty1"));
|
||||
assertEquals("jobProperty2value", step1.getProperty("jobProperty2"));
|
||||
|
||||
Properties job = batchPropertyContext.getJobProperties();
|
||||
assertEquals(3, job.size());
|
||||
assertEquals("decider1PropertyOverride", job.getProperty("deciderProperty1"));
|
||||
assertEquals("jobProperty1value", job.getProperty("jobProperty1"));
|
||||
assertEquals("jobProperty2value", job.getProperty("jobProperty2"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@
|
||||
<property name="annotationNamedDeciderPropertyName" value="annotationNamedDeciderPropertyValue"/>
|
||||
<property name="nonexistentDeciderPropertyName" value="nonexistentDeciderPropertyValue"/>
|
||||
</properties>
|
||||
<next on="*" to="#{jobProperties['step2name']}"/>
|
||||
<next on="*" to="#{jobParameters['unresolving.prop']}?:#{jobProperties['step2name']};"/>
|
||||
</decision>
|
||||
<step id="step2">
|
||||
<!-- JSR Section 8.2.3 -->
|
||||
@@ -132,7 +132,7 @@
|
||||
<bean id="injectTestObj" class="org.springframework.batch.core.jsr.configuration.xml.JobPropertyTests$InjectTestObj"
|
||||
c:name="Chris"/>
|
||||
|
||||
<bean id="threadLocalClassloaderBeanPostProcessor" class="org.springframework.batch.core.jsr.configuration.support.ThreadLocalClassloaderBeanPostProcessor">
|
||||
<bean id="jobParameterResolvingBeanFactoryPostProcessor" class="org.springframework.batch.core.jsr.configuration.support.JobParameterResolvingBeanFactoryPostProcessor">
|
||||
<constructor-arg>
|
||||
<props>
|
||||
<prop key="allow.start.if.complete">true</prop>
|
||||
|
||||
Reference in New Issue
Block a user