batchPropertyContextEntries) {
+ for (BatchPropertyContextEntry batchPropertyContextEntry : batchPropertyContextEntries) {
+ setBatchContextEntry(batchPropertyContextEntry);
+ }
+ }
- private void setBatchContextEntry(BatchPropertyContextEntry batchPropertyContextEntry) {
- String beanName = batchPropertyContextEntry.getBeanName();
- Properties properties = 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);
+ if (batchProperties.containsKey(beanName)) {
+ Properties existingProperties = batchProperties.get(beanName);
+ existingProperties.putAll(properties);
- batchProperties.put(beanName, existingProperties);
- } else {
- batchProperties.put(beanName, properties);
- }
- }
+ batchProperties.put(beanName, existingProperties);
+ } else {
+ batchProperties.put(beanName, properties);
+ }
+ }
/**
*
@@ -69,25 +70,33 @@ public class BatchPropertyContext {
* @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();
+ public Properties getBatchProperties(String beanName) {
+ Properties properties = new Properties();
- if (batchProperties.containsKey(beanName)) {
- properties.putAll(batchProperties.get(beanName));
- }
+ if (batchProperties.containsKey(beanName)) {
+ properties.putAll(batchProperties.get(beanName));
+ } else {
+ if(beanName.startsWith("scopedTarget")) {
+ beanName = beanName.substring(13);
+ }
- for (String jobLevelProperty : batchProperties.keySet()) {
- if (jobLevelProperty.startsWith("job-")) {
- if (batchProperties.containsKey(jobLevelProperty)) {
- properties.putAll(batchProperties.get(jobLevelProperty));
- }
+ if(batchProperties.containsKey(beanName)) {
+ properties.putAll(batchProperties.get(beanName));
+ }
+ }
- break;
- }
- }
+ for (String jobLevelProperty : batchProperties.keySet()) {
+ if (jobLevelProperty.startsWith("job-")) {
+ if (batchProperties.containsKey(jobLevelProperty)) {
+ properties.putAll(batchProperties.get(jobLevelProperty));
+ }
- return properties;
- }
+ break;
+ }
+ }
+
+ return properties;
+ }
/**
*
diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/jsr/configuration/support/ThreadLocalClassloaderBeanPostProcessor.java b/spring-batch-core/src/main/java/org/springframework/batch/core/jsr/configuration/support/ThreadLocalClassloaderBeanPostProcessor.java
new file mode 100644
index 000000000..95b8b0d8f
--- /dev/null
+++ b/spring-batch-core/src/main/java/org/springframework/batch/core/jsr/configuration/support/ThreadLocalClassloaderBeanPostProcessor.java
@@ -0,0 +1,76 @@
+/*
+ * 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 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.BeanFactoryPostProcessor;
+import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
+import org.springframework.beans.factory.config.RuntimeBeanReference;
+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;
+
+/**
+ * After the {@link BeanFactory} is created, this post processor will evaluate to see
+ * if any of the beans referenced from a job definition (as defined by JSR-352) point
+ * 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.
+ *
+ * @author Michael Minella
+ * @since 3.0
+ */
+public class ThreadLocalClassloaderBeanPostProcessor implements BeanFactoryPostProcessor, PriorityOrdered {
+
+ /* (non-Javadoc)
+ * @see org.springframework.beans.factory.config.BeanFactoryPostProcessor#postProcessBeanFactory(org.springframework.beans.factory.config.ConfigurableListableBeanFactory)
+ */
+ @Override
+ public void postProcessBeanFactory(
+ ConfigurableListableBeanFactory beanFactory) throws BeansException {
+ String [] beanNames = beanFactory.getBeanDefinitionNames();
+
+ for (String curName : beanNames) {
+ BeanDefinition beanDefinition = beanFactory.getBeanDefinition(curName);
+ PropertyValue[] values = beanDefinition.getPropertyValues().getPropertyValues();
+
+ for (PropertyValue propertyValue : values) {
+ Object value = propertyValue.getValue();
+
+ if(value instanceof RuntimeBeanReference) {
+ RuntimeBeanReference ref = (RuntimeBeanReference) value;
+ if(!beanFactory.containsBean(ref.getBeanName())) {
+ AbstractBeanDefinition newBeanDefinition = BeanDefinitionBuilder.genericBeanDefinition(ref.getBeanName()).getBeanDefinition();
+ newBeanDefinition.setScope("step");
+ ((DefaultListableBeanFactory) beanFactory).registerBeanDefinition(ref.getBeanName(), newBeanDefinition);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Sets this {@link BeanFactoryPostProcessor} to the lowest precdenece so that
+ * it is executed as late as possible in the chain of {@link BeanFactoryPostProcessor}s
+ */
+ @Override
+ public int getOrder() {
+ return PriorityOrdered.LOWEST_PRECEDENCE;
+ }
+}
diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/jsr/configuration/xml/JobParser.java b/spring-batch-core/src/main/java/org/springframework/batch/core/jsr/configuration/xml/JobParser.java
index f511bff80..e1798ba08 100644
--- a/spring-batch-core/src/main/java/org/springframework/batch/core/jsr/configuration/xml/JobParser.java
+++ b/spring-batch-core/src/main/java/org/springframework/batch/core/jsr/configuration/xml/JobParser.java
@@ -22,7 +22,6 @@ import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
-import org.springframework.context.annotation.AnnotationConfigUtils;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;
@@ -35,13 +34,10 @@ import org.w3c.dom.Element;
* @since 3.0
*/
public class JobParser extends AbstractSingleBeanDefinitionParser {
- private static final String ID_ATTRIBUTE = "id";
- private static final String RESTARTABLE_ATTRIBUTE = "restartable";
- private static final String BATCH_PROPERTY_POST_PROCESSOR_CLASS_NAME = "org.springframework.batch.core.jsr.configuration.support.BatchPropertyBeanPostProcessor";
- private static final String BATCH_PROPERTY_POST_PROCESSOR_BEAN_NAME = "batchPropertyPostProcessor";
- private static final String JSR_AUTOWIRED_ANNOTATION_BEAN_POST_PROCESSOR_CLASS_NAME = "org.springframework.batch.core.jsr.configuration.support.JsrAutowiredAnnotationBeanPostProcessor";
+ private static final String ID_ATTRIBUTE = "id";
+ private static final String RESTARTABLE_ATTRIBUTE = "restartable";
- @Override
+ @Override
protected Class getBeanClass(Element element) {
return JobFactoryBean.class;
}
@@ -49,7 +45,7 @@ public class JobParser extends AbstractSingleBeanDefinitionParser {
@Override
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
CoreNamespaceUtils.autoregisterBeansForNamespace(parserContext, parserContext.extractSource(element));
- autoregisterJsrBeansForNamespace(parserContext);
+ JsrNamespaceUtils.autoregisterJsrBeansForNamespace(parserContext);
String jobName = element.getAttribute(ID_ATTRIBUTE);
builder.addConstructorArgValue(jobName);
@@ -73,31 +69,4 @@ public class JobParser extends AbstractSingleBeanDefinitionParser {
new PropertyParser("job-" + jobName, parserContext).parseProperties(element);
}
- private void autoregisterJsrBeansForNamespace(ParserContext parserContext) {
- autoRegisterBatchPostProcessor(parserContext);
- autoRegisterJsrAutowiredAnnotationBeanPostProcessor(parserContext);
- }
-
- private void autoRegisterBatchPostProcessor(ParserContext parserContext) {
- BeanDefinitionBuilder batchPropertyBeanPostProcessor =
- BeanDefinitionBuilder.genericBeanDefinition(BATCH_PROPERTY_POST_PROCESSOR_CLASS_NAME);
-
- AbstractBeanDefinition batchPropertyBeanPostProcessorDefinition = batchPropertyBeanPostProcessor.getBeanDefinition();
- batchPropertyBeanPostProcessorDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
-
- parserContext.getRegistry().registerBeanDefinition(BATCH_PROPERTY_POST_PROCESSOR_BEAN_NAME, batchPropertyBeanPostProcessorDefinition);
- }
-
- private void autoRegisterJsrAutowiredAnnotationBeanPostProcessor(ParserContext parserContext) {
- BeanDefinitionBuilder jsrAutowiredAnnotationBeanPostProcessor =
- BeanDefinitionBuilder.genericBeanDefinition(JSR_AUTOWIRED_ANNOTATION_BEAN_POST_PROCESSOR_CLASS_NAME);
-
- AbstractBeanDefinition jsrAutowiredAnnotationBeanPostProcessorDefinition =
- jsrAutowiredAnnotationBeanPostProcessor.getBeanDefinition();
-
- jsrAutowiredAnnotationBeanPostProcessorDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
-
- parserContext.getRegistry().registerBeanDefinition(AnnotationConfigUtils.AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME,
- jsrAutowiredAnnotationBeanPostProcessorDefinition);
- }
}
diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/jsr/configuration/xml/JsrNamespaceUtils.java b/spring-batch-core/src/main/java/org/springframework/batch/core/jsr/configuration/xml/JsrNamespaceUtils.java
new file mode 100644
index 000000000..9d4fbd5e7
--- /dev/null
+++ b/spring-batch-core/src/main/java/org/springframework/batch/core/jsr/configuration/xml/JsrNamespaceUtils.java
@@ -0,0 +1,67 @@
+/*
+ * 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.xml;
+
+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;
+import org.springframework.beans.factory.xml.ParserContext;
+import org.springframework.context.annotation.AnnotationConfigUtils;
+
+/**
+ * Utility methods used in parsing of the JSR-352 batch namespace
+ *
+ * @author Michael Minella
+ * @since 3.0
+ */
+class JsrNamespaceUtils {
+ private static final String BATCH_PROPERTY_POST_PROCESSOR_BEAN_NAME = "batchPropertyPostProcessor";
+ private static final String THREAD_LOCAL_CLASSLOASER_BEAN_POST_PROCESSOR_BEAN_NAME = "threadLocalClassloaderBeanPostProcessor";
+
+ static void autoregisterJsrBeansForNamespace(ParserContext parserContext) {
+ autoRegisterBatchPostProcessor(parserContext);
+ autoRegisterJsrAutowiredAnnotationBeanPostProcessor(parserContext);
+ autoRegisterThreadLocalClassloaderBeanPostProcessor(parserContext);
+ }
+
+ private static void autoRegisterThreadLocalClassloaderBeanPostProcessor(ParserContext parserContext) {
+ registerPostProcessor(parserContext, ThreadLocalClassloaderBeanPostProcessor.class, BeanDefinition.ROLE_INFRASTRUCTURE, THREAD_LOCAL_CLASSLOASER_BEAN_POST_PROCESSOR_BEAN_NAME);
+ }
+
+ private static void autoRegisterBatchPostProcessor(ParserContext parserContext) {
+ registerPostProcessor(parserContext, BatchPropertyBeanPostProcessor.class, BeanDefinition.ROLE_INFRASTRUCTURE, BATCH_PROPERTY_POST_PROCESSOR_BEAN_NAME);
+ }
+
+ private static void autoRegisterJsrAutowiredAnnotationBeanPostProcessor(ParserContext parserContext) {
+ registerPostProcessor(parserContext, JsrAutowiredAnnotationBeanPostProcessor.class, BeanDefinition.ROLE_INFRASTRUCTURE, AnnotationConfigUtils.AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME);
+ }
+
+ private static void registerPostProcessor(ParserContext parserContext, Class> clazz, int role, String beanName) {
+ BeanDefinitionBuilder jsrAutowiredAnnotationBeanPostProcessor =
+ BeanDefinitionBuilder.genericBeanDefinition(clazz);
+
+ AbstractBeanDefinition jsrAutowiredAnnotationBeanPostProcessorDefinition =
+ jsrAutowiredAnnotationBeanPostProcessor.getBeanDefinition();
+
+ jsrAutowiredAnnotationBeanPostProcessorDefinition.setRole(role);
+
+ parserContext.getRegistry().registerBeanDefinition(beanName,
+ jsrAutowiredAnnotationBeanPostProcessorDefinition);
+ }
+}
diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/jsr/configuration/xml/ThreadLocalClassloaderBeanPostProcessorTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/jsr/configuration/xml/ThreadLocalClassloaderBeanPostProcessorTests.java
new file mode 100644
index 000000000..86cbee342
--- /dev/null
+++ b/spring-batch-core/src/test/java/org/springframework/batch/core/jsr/configuration/xml/ThreadLocalClassloaderBeanPostProcessorTests.java
@@ -0,0 +1,44 @@
+/*
+ * 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.xml;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.Properties;
+
+import javax.batch.operations.JobOperator;
+import javax.batch.runtime.BatchRuntime;
+import javax.batch.runtime.BatchStatus;
+
+import org.junit.Before;
+import org.junit.Test;
+
+public class ThreadLocalClassloaderBeanPostProcessorTests {
+
+ private JobOperator jobOperator;
+
+ @Before
+ public void setUp() throws Exception {
+ jobOperator = BatchRuntime.getJobOperator();
+ }
+
+ @Test
+ public void test() throws Exception {
+ long executionId = jobOperator.start("threadLocalClassloaderBeanPostProcessorTestsJob", new Properties());
+
+ assertEquals(BatchStatus.COMPLETED, jobOperator.getJobExecution(executionId).getBatchStatus());
+ }
+}
diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/jsr/configuration/xml/ThreadLocalClassloaderBeanPostProcessorTestsBatchlet.java b/spring-batch-core/src/test/java/org/springframework/batch/core/jsr/configuration/xml/ThreadLocalClassloaderBeanPostProcessorTestsBatchlet.java
new file mode 100644
index 000000000..3a2dc092b
--- /dev/null
+++ b/spring-batch-core/src/test/java/org/springframework/batch/core/jsr/configuration/xml/ThreadLocalClassloaderBeanPostProcessorTestsBatchlet.java
@@ -0,0 +1,49 @@
+/*
+ * 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.xml;
+
+import javax.batch.api.BatchProperty;
+import javax.batch.api.Batchlet;
+import javax.batch.runtime.context.JobContext;
+import javax.batch.runtime.context.StepContext;
+import javax.inject.Inject;
+
+import org.springframework.util.Assert;
+
+public class ThreadLocalClassloaderBeanPostProcessorTestsBatchlet implements Batchlet {
+ @Inject
+ @BatchProperty
+ public String jobParam1;
+
+ @Inject
+ public JobContext jobContext;
+
+ @Inject
+ public StepContext stepContext;
+
+ @Override
+ public String process() throws Exception {
+ Assert.isTrue("someParameter".equals(jobParam1), jobParam1 + " does not equal someParamter");
+ Assert.isTrue("threadLocalClassloaderBeanPostProcessorTestsJob".equals(jobContext.getJobName()));
+ Assert.isTrue("step1".equals(stepContext.getStepName()));
+
+ return null;
+ }
+
+ @Override
+ public void stop() throws Exception {
+ }
+}
diff --git a/spring-batch-core/src/test/resources/META-INF/batch-jobs/threadLocalClassloaderBeanPostProcessorTestsJob.xml b/spring-batch-core/src/test/resources/META-INF/batch-jobs/threadLocalClassloaderBeanPostProcessorTestsJob.xml
new file mode 100644
index 000000000..e542a5d7a
--- /dev/null
+++ b/spring-batch-core/src/test/resources/META-INF/batch-jobs/threadLocalClassloaderBeanPostProcessorTestsJob.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+