BATCH-2001 JSR Property support.
This commit is contained in:
committed by
Michael Minella
parent
60709cd923
commit
c6fabd72a6
@@ -130,6 +130,12 @@
|
||||
<artifactId>mockito-all</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.inject</groupId>
|
||||
<artifactId>javax.inject</artifactId>
|
||||
<version>1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
|
||||
@@ -21,7 +21,6 @@ import javax.batch.runtime.BatchStatus;
|
||||
|
||||
import org.springframework.batch.core.ExitStatus;
|
||||
import org.springframework.batch.core.JobExecution;
|
||||
import org.springframework.batch.core.converter.JobParametersConverter;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -30,23 +29,23 @@ import org.springframework.util.Assert;
|
||||
* obtain the related contextual information.
|
||||
*
|
||||
* @author Michael Minella
|
||||
* @author Chris Schaefer
|
||||
* @since 3.0
|
||||
*/
|
||||
public class JobContext implements javax.batch.runtime.context.JobContext {
|
||||
|
||||
private JobExecution jobExecution;
|
||||
private Object transientUserData;
|
||||
private JobParametersConverter jobParametersConverter;
|
||||
private Properties properties;
|
||||
private JobExecution jobExecution;
|
||||
|
||||
/**
|
||||
/**
|
||||
* @param jobExecution for the related job
|
||||
*/
|
||||
public JobContext(JobExecution jobExecution, JobParametersConverter jobParametersConverter) {
|
||||
public JobContext(JobExecution jobExecution, Properties properties) {
|
||||
Assert.notNull(jobExecution, "A JobExecution is required");
|
||||
Assert.notNull(jobParametersConverter, "A ParametersConverter is required");
|
||||
|
||||
this.jobExecution = jobExecution;
|
||||
this.jobParametersConverter = jobParametersConverter;
|
||||
this.properties = properties;
|
||||
this.jobExecution = jobExecution;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
@@ -94,7 +93,7 @@ public class JobContext implements javax.batch.runtime.context.JobContext {
|
||||
*/
|
||||
@Override
|
||||
public Properties getProperties() {
|
||||
return jobParametersConverter.getProperties(this.jobExecution.getJobParameters());
|
||||
return properties != null ? properties : new Properties();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
|
||||
@@ -24,7 +24,6 @@ import javax.batch.runtime.Metric;
|
||||
|
||||
import org.springframework.batch.core.ExitStatus;
|
||||
import org.springframework.batch.core.StepExecution;
|
||||
import org.springframework.batch.core.converter.JobParametersConverter;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -33,20 +32,20 @@ import org.springframework.util.Assert;
|
||||
* obtain the related contextual information.
|
||||
*
|
||||
* @author Michael Minella
|
||||
* @author Chris Schaefer
|
||||
* @since 3.0
|
||||
*/
|
||||
public class StepContext implements javax.batch.runtime.context.StepContext {
|
||||
|
||||
private StepExecution stepExecution;
|
||||
private Object transientUserData;
|
||||
private JobParametersConverter jobParametersConveter;
|
||||
private Properties properties = new Properties();
|
||||
|
||||
public StepContext(StepExecution stepExecution, JobParametersConverter jobParametersConveter) {
|
||||
public StepContext(StepExecution stepExecution, Properties properties) {
|
||||
Assert.notNull(stepExecution, "A StepExecution is required");
|
||||
Assert.notNull(jobParametersConveter, "A ParametersConverter is required");
|
||||
|
||||
this.stepExecution = stepExecution;
|
||||
this.jobParametersConveter = jobParametersConveter;
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
@@ -86,8 +85,7 @@ public class StepContext implements javax.batch.runtime.context.StepContext {
|
||||
*/
|
||||
@Override
|
||||
public Properties getProperties() {
|
||||
//TODO: Fix this...this should be properties, not parameters. Waiting on BATCH-2001
|
||||
return jobParametersConveter.getProperties(this.stepExecution.getJobParameters());
|
||||
return properties != null ? properties : new Properties();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
|
||||
@@ -15,49 +15,39 @@
|
||||
*/
|
||||
package org.springframework.batch.core.jsr;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.springframework.batch.core.converter.JobParametersConverter;
|
||||
import org.springframework.batch.core.jsr.configuration.support.BatchPropertyContext;
|
||||
import org.springframework.batch.core.scope.context.StepSynchronizationManager;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link FactoryBean} implementation used to create {@link javax.batch.runtime.context.StepContext}
|
||||
* instances within the step scope.
|
||||
*
|
||||
* @author Michael Minella
|
||||
* @author Chris Schaefer
|
||||
* @since 3.0
|
||||
*/
|
||||
public class StepContextFactoryBean implements FactoryBean<StepContext>, InitializingBean {
|
||||
|
||||
public class StepContextFactoryBean implements FactoryBean<StepContext> {
|
||||
@Autowired
|
||||
public DataSource dataSource;
|
||||
private JobParametersConverter jobParametersConveter;
|
||||
private BatchPropertyContext batchPropertyContext;
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
|
||||
*/
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.notNull(dataSource, "A DataSource is required");
|
||||
|
||||
jobParametersConveter = new JsrJobParametersConverter(dataSource);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.FactoryBean#getObject()
|
||||
*/
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.FactoryBean#getObject()
|
||||
*/
|
||||
@Override
|
||||
public StepContext getObject() throws Exception {
|
||||
return new StepContext(StepSynchronizationManager.getContext().getStepExecution(), jobParametersConveter);
|
||||
org.springframework.batch.core.StepExecution stepExecution = StepSynchronizationManager.getContext().getStepExecution();
|
||||
Properties properties = batchPropertyContext.getBatchProperties(stepExecution.getStepName());
|
||||
|
||||
return new StepContext(stepExecution, properties);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.FactoryBean#getObjectType()
|
||||
*/
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.FactoryBean#getObjectType()
|
||||
*/
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
return StepContext.class;
|
||||
|
||||
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* 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.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.HashSet;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import javax.batch.api.BatchProperty;
|
||||
import javax.batch.api.Batchlet;
|
||||
import javax.batch.api.chunk.ItemProcessor;
|
||||
import javax.batch.api.chunk.ItemReader;
|
||||
import javax.batch.api.chunk.ItemWriter;
|
||||
import javax.batch.api.chunk.listener.ItemProcessListener;
|
||||
import javax.batch.api.chunk.listener.ItemReadListener;
|
||||
import javax.batch.api.chunk.listener.ItemWriteListener;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.batch.core.Job;
|
||||
import org.springframework.batch.core.Step;
|
||||
import org.springframework.batch.core.job.flow.JobExecutionDecider;
|
||||
import org.springframework.batch.repeat.CompletionPolicy;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* {@link BeanPostProcessor} implementation used to inject JSR-352 String properties into batch artifact fields
|
||||
* that are marked with the {@link BatchProperty} annotation.
|
||||
* </p>
|
||||
*
|
||||
* @author Chris Schaefer
|
||||
* @since 3.0
|
||||
*/
|
||||
public class BatchPropertyBeanPostProcessor implements BeanPostProcessor {
|
||||
@Autowired
|
||||
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;
|
||||
}
|
||||
|
||||
final Properties artifactProperties = batchPropertyContext.getBatchProperties(beanName);
|
||||
|
||||
if (artifactProperties.isEmpty()) {
|
||||
return bean;
|
||||
}
|
||||
|
||||
injectBatchProperties(bean, artifactProperties);
|
||||
|
||||
return bean;
|
||||
}
|
||||
|
||||
private void setRequiredAnnotations() {
|
||||
ClassLoader cl = BatchPropertyBeanPostProcessor.class.getClassLoader();
|
||||
|
||||
try {
|
||||
this.requiredAnnotations.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.");
|
||||
}
|
||||
|
||||
this.requiredAnnotations.add(BatchProperty.class);
|
||||
}
|
||||
|
||||
private boolean isBatchArtifact(Object bean) {
|
||||
return (bean instanceof ItemReader) ||
|
||||
(bean instanceof ItemProcessor) ||
|
||||
(bean instanceof ItemWriter) ||
|
||||
(bean instanceof CompletionPolicy) ||
|
||||
(bean instanceof Batchlet) ||
|
||||
(bean instanceof ItemReadListener) ||
|
||||
(bean instanceof ItemProcessListener) ||
|
||||
(bean instanceof ItemWriteListener) ||
|
||||
(bean instanceof JobExecutionDecider) ||
|
||||
(bean instanceof Step) ||
|
||||
(bean instanceof Job);
|
||||
}
|
||||
|
||||
private void injectBatchProperties(final Object bean, final Properties artifactProperties) {
|
||||
ReflectionUtils.doWithFields(bean.getClass(), new ReflectionUtils.FieldCallback() {
|
||||
public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
|
||||
if (isValidFieldModifier(field) && isAnnotated(field)) {
|
||||
boolean isAccessible = field.isAccessible();
|
||||
field.setAccessible(true);
|
||||
|
||||
String batchProperty = getBatchPropertyFieldValue(field, artifactProperties);
|
||||
|
||||
if (batchProperty != null) {
|
||||
field.set(bean, batchProperty);
|
||||
}
|
||||
|
||||
field.setAccessible(isAccessible);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private String getBatchPropertyFieldValue(Field field, Properties batchArtifactProperties) {
|
||||
BatchProperty batchProperty = field.getAnnotation(BatchProperty.class);
|
||||
|
||||
if (!"".equals(batchProperty.name())) {
|
||||
return getBatchProperty(batchProperty.name(), batchArtifactProperties);
|
||||
}
|
||||
|
||||
return getBatchProperty(field.getName(), batchArtifactProperties);
|
||||
}
|
||||
|
||||
private String getBatchProperty(String propertyKey, Properties batchArtifactProperties) {
|
||||
if (batchArtifactProperties.containsKey(propertyKey)) {
|
||||
return (String) batchArtifactProperties.get(propertyKey);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean isAnnotated(Field field) {
|
||||
for (Class<? extends Annotation> annotation : requiredAnnotations) {
|
||||
if(!field.isAnnotationPresent(annotation)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean isValidFieldModifier(Field field) {
|
||||
return !Modifier.isStatic(field.getModifiers()) && !Modifier.isFinal(field.getModifiers());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
|
||||
return bean;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* 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.List;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* <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.
|
||||
* </p>
|
||||
*
|
||||
* @author Chris Schaefer
|
||||
* @since 3.0
|
||||
*/
|
||||
public class BatchPropertyContext {
|
||||
private ConcurrentHashMap<String, Properties> batchProperties = new ConcurrentHashMap<String, Properties>();
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Adds each of the provided {@link BatchPropertyContext} objects to the existing propery
|
||||
* context.
|
||||
* </p>
|
||||
*
|
||||
* @param batchPropertyContextEntries the {@link BatchPropertyContextEntry} objects to add
|
||||
*/
|
||||
public void setBatchContextEntries(List<BatchPropertyContextEntry> batchPropertyContextEntries) {
|
||||
for (BatchPropertyContextEntry batchPropertyContextEntry : batchPropertyContextEntries) {
|
||||
setBatchContextEntry(batchPropertyContextEntry);
|
||||
}
|
||||
}
|
||||
|
||||
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 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.
|
||||
* </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));
|
||||
}
|
||||
|
||||
for (String jobLevelProperty : batchProperties.keySet()) {
|
||||
if (jobLevelProperty.startsWith("job-")) {
|
||||
if (batchProperties.containsKey(jobLevelProperty)) {
|
||||
properties.putAll(batchProperties.get(jobLevelProperty));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Simple object to encapsulate batch properties of a given bean / batch artifact.
|
||||
* </p>
|
||||
*
|
||||
* @author Chris Schaefer
|
||||
* @since 3.0
|
||||
*/
|
||||
public class BatchPropertyContextEntry {
|
||||
private String beanName;
|
||||
private Properties properties;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Creates a new entry instance using the provided bean name representing batch artifact
|
||||
* and its associated {@link Properties}.
|
||||
* </p>
|
||||
*
|
||||
* @param beanName the bean name representing the batch artifact
|
||||
* @param properties the associated {@link Properties}
|
||||
*/
|
||||
public BatchPropertyContextEntry(String beanName, Properties properties) {
|
||||
this.beanName = beanName;
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Obtains the bean name of the batch artifact this entry is associated with.
|
||||
* </p>
|
||||
*
|
||||
* @return the bean name of the batch artifact
|
||||
*/
|
||||
public String getBeanName() {
|
||||
return beanName;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Obtains the batch {@link Properties} that are associated with this entry.
|
||||
* </p>
|
||||
*
|
||||
* @return the batch {@link Properties}
|
||||
*/
|
||||
public Properties getProperties() {
|
||||
return properties;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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.lang.annotation.Annotation;
|
||||
import java.lang.reflect.AccessibleObject;
|
||||
import javax.batch.api.BatchProperty;
|
||||
import org.springframework.beans.factory.annotation.InjectionMetadata;
|
||||
|
||||
/**
|
||||
* <p>This class overrides methods in the copied {@link SpringAutowiredAnnotationBeanPostProcessor} class
|
||||
* to check for the {@link @BatchProperty} annotation before processing injection annotations. If the annotation
|
||||
* is found, further injection processing for the field is skipped.</p>
|
||||
*/
|
||||
public class JsrAutowiredAnnotationBeanPostProcessor extends SpringAutowiredAnnotationBeanPostProcessor {
|
||||
@Override
|
||||
protected InjectionMetadata findAutowiringMetadata(Class<?> clazz) {
|
||||
return super.buildAutowiringMetadata(clazz);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Annotation findAutowiredAnnotation(AccessibleObject ao) {
|
||||
if (ao.getAnnotation(BatchProperty.class) != null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return super.findAutowiredAnnotation(ao);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,585 @@
|
||||
/*
|
||||
* 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.beans.PropertyDescriptor;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.AccessibleObject;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.PropertyValues;
|
||||
import org.springframework.beans.TypeConverter;
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.BeanFactoryUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.InjectionMetadata;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.config.DependencyDescriptor;
|
||||
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.core.BridgeMethodResolver;
|
||||
import org.springframework.core.GenericTypeResolver;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.PriorityOrdered;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* <p>This is a copy of AutowiredAnnotationBeanPostProcessor with modifications allow a subclass to
|
||||
* do additional checks on other field annotations before processing injection annotations.</p>
|
||||
*
|
||||
* <p>This class is considered a quick work around and needs to be refactored / removed.</p>
|
||||
*
|
||||
* <p>The in addition to making this class package private, the following methods were modified to be protected:</p>
|
||||
* <li>findAutowiringMetadata(Class<?> clazz)</li>
|
||||
* <li>buildAutowiringMetadata(Class<?> clazz)</li>
|
||||
* <li>findAutowiredAnnotation(AccessibleObject ao)</li>
|
||||
*/
|
||||
class SpringAutowiredAnnotationBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter
|
||||
implements MergedBeanDefinitionPostProcessor, PriorityOrdered, BeanFactoryAware {
|
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private final Set<Class<? extends Annotation>> autowiredAnnotationTypes =
|
||||
new LinkedHashSet<Class<? extends Annotation>>();
|
||||
|
||||
private String requiredParameterName = "required";
|
||||
|
||||
private boolean requiredParameterValue = true;
|
||||
|
||||
private int order = Ordered.LOWEST_PRECEDENCE - 2;
|
||||
|
||||
private ConfigurableListableBeanFactory beanFactory;
|
||||
|
||||
private final Map<Class<?>, Constructor<?>[]> candidateConstructorsCache =
|
||||
new ConcurrentHashMap<Class<?>, Constructor<?>[]>(64);
|
||||
|
||||
private final Map<Class<?>, InjectionMetadata> injectionMetadataCache =
|
||||
new ConcurrentHashMap<Class<?>, InjectionMetadata>(64);
|
||||
|
||||
|
||||
/**
|
||||
* Create a new AutowiredAnnotationBeanPostProcessor
|
||||
* for Spring's standard {@link org.springframework.beans.factory.annotation.Autowired} annotation.
|
||||
* <p>Also supports JSR-330's {@link javax.inject.Inject} annotation, if available.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public SpringAutowiredAnnotationBeanPostProcessor() {
|
||||
this.autowiredAnnotationTypes.add(Autowired.class);
|
||||
this.autowiredAnnotationTypes.add(Value.class);
|
||||
ClassLoader cl = SpringAutowiredAnnotationBeanPostProcessor.class.getClassLoader();
|
||||
try {
|
||||
this.autowiredAnnotationTypes.add((Class<? extends Annotation>) cl.loadClass("javax.inject.Inject"));
|
||||
logger.info("JSR-330 'javax.inject.Inject' annotation found and supported for autowiring");
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
// JSR-330 API not available - simply skip.
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the 'autowired' annotation type, to be used on constructors, fields,
|
||||
* setter methods and arbitrary config methods.
|
||||
* <p>The default autowired annotation type is the Spring-provided
|
||||
* {@link Autowired} annotation, as well as {@link Value}.
|
||||
* <p>This setter property exists so that developers can provide their own
|
||||
* (non-Spring-specific) annotation type to indicate that a member is
|
||||
* supposed to be autowired.
|
||||
*/
|
||||
public void setAutowiredAnnotationType(Class<? extends Annotation> autowiredAnnotationType) {
|
||||
Assert.notNull(autowiredAnnotationType, "'autowiredAnnotationType' must not be null");
|
||||
this.autowiredAnnotationTypes.clear();
|
||||
this.autowiredAnnotationTypes.add(autowiredAnnotationType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the 'autowired' annotation types, to be used on constructors, fields,
|
||||
* setter methods and arbitrary config methods.
|
||||
* <p>The default autowired annotation type is the Spring-provided
|
||||
* {@link Autowired} annotation, as well as {@link Value}.
|
||||
* <p>This setter property exists so that developers can provide their own
|
||||
* (non-Spring-specific) annotation types to indicate that a member is
|
||||
* supposed to be autowired.
|
||||
*/
|
||||
public void setAutowiredAnnotationTypes(Set<Class<? extends Annotation>> autowiredAnnotationTypes) {
|
||||
Assert.notEmpty(autowiredAnnotationTypes, "'autowiredAnnotationTypes' must not be empty");
|
||||
this.autowiredAnnotationTypes.clear();
|
||||
this.autowiredAnnotationTypes.addAll(autowiredAnnotationTypes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name of a parameter of the annotation that specifies
|
||||
* whether it is required.
|
||||
* @see #setRequiredParameterValue(boolean)
|
||||
*/
|
||||
public void setRequiredParameterName(String requiredParameterName) {
|
||||
this.requiredParameterName = requiredParameterName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the boolean value that marks a dependency as required
|
||||
* <p>For example if using 'required=true' (the default),
|
||||
* this value should be <code>true</code>; but if using
|
||||
* 'optional=false', this value should be <code>false</code>.
|
||||
* @see #setRequiredParameterName(String)
|
||||
*/
|
||||
public void setRequiredParameterValue(boolean requiredParameterValue) {
|
||||
this.requiredParameterValue = requiredParameterValue;
|
||||
}
|
||||
|
||||
public void setOrder(int order) {
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
public int getOrder() {
|
||||
return this.order;
|
||||
}
|
||||
|
||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||
if (!(beanFactory instanceof ConfigurableListableBeanFactory)) {
|
||||
throw new IllegalArgumentException(
|
||||
"AutowiredAnnotationBeanPostProcessor requires a ConfigurableListableBeanFactory");
|
||||
}
|
||||
this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
|
||||
}
|
||||
|
||||
|
||||
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
|
||||
if (beanType != null) {
|
||||
InjectionMetadata metadata = findAutowiringMetadata(beanType);
|
||||
metadata.checkConfigMembers(beanDefinition);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, String beanName) throws BeansException {
|
||||
// Quick check on the concurrent map first, with minimal locking.
|
||||
Constructor<?>[] candidateConstructors = this.candidateConstructorsCache.get(beanClass);
|
||||
if (candidateConstructors == null) {
|
||||
synchronized (this.candidateConstructorsCache) {
|
||||
candidateConstructors = this.candidateConstructorsCache.get(beanClass);
|
||||
if (candidateConstructors == null) {
|
||||
Constructor<?>[] rawCandidates = beanClass.getDeclaredConstructors();
|
||||
List<Constructor<?>> candidates = new ArrayList<Constructor<?>>(rawCandidates.length);
|
||||
Constructor<?> requiredConstructor = null;
|
||||
Constructor<?> defaultConstructor = null;
|
||||
for (Constructor<?> candidate : rawCandidates) {
|
||||
Annotation annotation = findAutowiredAnnotation(candidate);
|
||||
if (annotation != null) {
|
||||
if (requiredConstructor != null) {
|
||||
throw new BeanCreationException("Invalid autowire-marked constructor: " + candidate +
|
||||
". Found another constructor with 'required' Autowired annotation: " +
|
||||
requiredConstructor);
|
||||
}
|
||||
if (candidate.getParameterTypes().length == 0) {
|
||||
throw new IllegalStateException(
|
||||
"Autowired annotation requires at least one argument: " + candidate);
|
||||
}
|
||||
boolean required = determineRequiredStatus(annotation);
|
||||
if (required) {
|
||||
if (!candidates.isEmpty()) {
|
||||
throw new BeanCreationException(
|
||||
"Invalid autowire-marked constructors: " + candidates +
|
||||
". Found another constructor with 'required' Autowired annotation: " +
|
||||
requiredConstructor);
|
||||
}
|
||||
requiredConstructor = candidate;
|
||||
}
|
||||
candidates.add(candidate);
|
||||
}
|
||||
else if (candidate.getParameterTypes().length == 0) {
|
||||
defaultConstructor = candidate;
|
||||
}
|
||||
}
|
||||
if (!candidates.isEmpty()) {
|
||||
// Add default constructor to list of optional constructors, as fallback.
|
||||
if (requiredConstructor == null && defaultConstructor != null) {
|
||||
candidates.add(defaultConstructor);
|
||||
}
|
||||
candidateConstructors = candidates.toArray(new Constructor[candidates.size()]);
|
||||
}
|
||||
else {
|
||||
candidateConstructors = new Constructor[0];
|
||||
}
|
||||
this.candidateConstructorsCache.put(beanClass, candidateConstructors);
|
||||
}
|
||||
}
|
||||
}
|
||||
return (candidateConstructors.length > 0 ? candidateConstructors : null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PropertyValues postProcessPropertyValues(
|
||||
PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
|
||||
|
||||
InjectionMetadata metadata = findAutowiringMetadata(bean.getClass());
|
||||
try {
|
||||
metadata.inject(bean, beanName, pvs);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
|
||||
}
|
||||
return pvs;
|
||||
}
|
||||
|
||||
/**
|
||||
* 'Native' processing method for direct calls with an arbitrary target instance,
|
||||
* resolving all of its fields and methods which are annotated with <code>@Autowired</code>.
|
||||
* @param bean the target instance to process
|
||||
* @throws BeansException if autowiring failed
|
||||
*/
|
||||
public void processInjection(Object bean) throws BeansException {
|
||||
Class<?> clazz = bean.getClass();
|
||||
InjectionMetadata metadata = findAutowiringMetadata(clazz);
|
||||
try {
|
||||
metadata.inject(bean, null, null);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
throw new BeanCreationException("Injection of autowired dependencies failed for class [" + clazz + "]", ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected InjectionMetadata findAutowiringMetadata(Class<?> clazz) {
|
||||
// Quick check on the concurrent map first, with minimal locking.
|
||||
InjectionMetadata metadata = this.injectionMetadataCache.get(clazz);
|
||||
if (metadata == null) {
|
||||
synchronized (this.injectionMetadataCache) {
|
||||
metadata = this.injectionMetadataCache.get(clazz);
|
||||
if (metadata == null) {
|
||||
metadata = buildAutowiringMetadata(clazz);
|
||||
this.injectionMetadataCache.put(clazz, metadata);
|
||||
}
|
||||
}
|
||||
}
|
||||
return metadata;
|
||||
}
|
||||
|
||||
protected InjectionMetadata buildAutowiringMetadata(Class<?> clazz) {
|
||||
LinkedList<InjectionMetadata.InjectedElement> elements = new LinkedList<InjectionMetadata.InjectedElement>();
|
||||
Class<?> targetClass = clazz;
|
||||
|
||||
do {
|
||||
LinkedList<InjectionMetadata.InjectedElement> currElements = new LinkedList<InjectionMetadata.InjectedElement>();
|
||||
for (Field field : targetClass.getDeclaredFields()) {
|
||||
Annotation annotation = findAutowiredAnnotation(field);
|
||||
if (annotation != null) {
|
||||
if (Modifier.isStatic(field.getModifiers())) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("Autowired annotation is not supported on static fields: " + field);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
boolean required = determineRequiredStatus(annotation);
|
||||
currElements.add(new AutowiredFieldElement(field, required));
|
||||
}
|
||||
}
|
||||
for (Method method : targetClass.getDeclaredMethods()) {
|
||||
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
|
||||
Annotation annotation = BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod) ?
|
||||
findAutowiredAnnotation(bridgedMethod) : findAutowiredAnnotation(method);
|
||||
if (annotation != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
|
||||
if (Modifier.isStatic(method.getModifiers())) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("Autowired annotation is not supported on static methods: " + method);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (method.getParameterTypes().length == 0) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("Autowired annotation should be used on methods with actual parameters: " + method);
|
||||
}
|
||||
}
|
||||
boolean required = determineRequiredStatus(annotation);
|
||||
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(method);
|
||||
currElements.add(new AutowiredMethodElement(method, required, pd));
|
||||
}
|
||||
}
|
||||
elements.addAll(0, currElements);
|
||||
targetClass = targetClass.getSuperclass();
|
||||
}
|
||||
while (targetClass != null && targetClass != Object.class);
|
||||
|
||||
return new InjectionMetadata(clazz, elements);
|
||||
}
|
||||
|
||||
protected Annotation findAutowiredAnnotation(AccessibleObject ao) {
|
||||
for (Class<? extends Annotation> type : this.autowiredAnnotationTypes) {
|
||||
Annotation annotation = AnnotationUtils.getAnnotation(ao, type);
|
||||
if (annotation != null) {
|
||||
return annotation;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain all beans of the given type as autowire candidates.
|
||||
* @param type the type of the bean
|
||||
* @return the target beans, or an empty Collection if no bean of this type is found
|
||||
* @throws BeansException if bean retrieval failed
|
||||
*/
|
||||
protected <T> Map<String, T> findAutowireCandidates(Class<T> type) throws BeansException {
|
||||
if (this.beanFactory == null) {
|
||||
throw new IllegalStateException("No BeanFactory configured - " +
|
||||
"override the getBeanOfType method or specify the 'beanFactory' property");
|
||||
}
|
||||
return BeanFactoryUtils.beansOfTypeIncludingAncestors(this.beanFactory, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the annotated field or method requires its dependency.
|
||||
* <p>A 'required' dependency means that autowiring should fail when no beans
|
||||
* are found. Otherwise, the autowiring process will simply bypass the field
|
||||
* or method when no beans are found.
|
||||
* @param annotation the Autowired annotation
|
||||
* @return whether the annotation indicates that a dependency is required
|
||||
*/
|
||||
protected boolean determineRequiredStatus(Annotation annotation) {
|
||||
try {
|
||||
Method method = ReflectionUtils.findMethod(annotation.annotationType(), this.requiredParameterName);
|
||||
if (method == null) {
|
||||
// annotations like @Inject and @Value don't have a method (attribute) named "required"
|
||||
// -> default to required status
|
||||
return true;
|
||||
}
|
||||
return (this.requiredParameterValue == (Boolean) ReflectionUtils.invokeMethod(method, annotation));
|
||||
}
|
||||
catch (Exception ex) {
|
||||
// an exception was thrown during reflective invocation of the required attribute
|
||||
// -> default to required status
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the specified bean as dependent on the autowired beans.
|
||||
*/
|
||||
private void registerDependentBeans(String beanName, Set<String> autowiredBeanNames) {
|
||||
if (beanName != null) {
|
||||
for (String autowiredBeanName : autowiredBeanNames) {
|
||||
if (this.beanFactory.containsBean(autowiredBeanName)) {
|
||||
this.beanFactory.registerDependentBean(autowiredBeanName, beanName);
|
||||
}
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Autowiring by type from bean name '" + beanName +
|
||||
"' to bean named '" + autowiredBeanName + "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the specified cached method argument or field value.
|
||||
*/
|
||||
private Object resolvedCachedArgument(String beanName, Object cachedArgument) {
|
||||
if (cachedArgument instanceof DependencyDescriptor) {
|
||||
DependencyDescriptor descriptor = (DependencyDescriptor) cachedArgument;
|
||||
TypeConverter typeConverter = this.beanFactory.getTypeConverter();
|
||||
return this.beanFactory.resolveDependency(descriptor, beanName, null, typeConverter);
|
||||
}
|
||||
else if (cachedArgument instanceof RuntimeBeanReference) {
|
||||
return this.beanFactory.getBean(((RuntimeBeanReference) cachedArgument).getBeanName());
|
||||
}
|
||||
else {
|
||||
return cachedArgument;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Class representing injection information about an annotated field.
|
||||
*/
|
||||
private class AutowiredFieldElement extends InjectionMetadata.InjectedElement {
|
||||
|
||||
private final boolean required;
|
||||
|
||||
private volatile boolean cached = false;
|
||||
|
||||
private volatile Object cachedFieldValue;
|
||||
|
||||
public AutowiredFieldElement(Field field, boolean required) {
|
||||
super(field, null);
|
||||
this.required = required;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable {
|
||||
Field field = (Field) this.member;
|
||||
try {
|
||||
Object value;
|
||||
if (this.cached) {
|
||||
value = resolvedCachedArgument(beanName, this.cachedFieldValue);
|
||||
}
|
||||
else {
|
||||
DependencyDescriptor descriptor = new DependencyDescriptor(field, this.required);
|
||||
Set<String> autowiredBeanNames = new LinkedHashSet<String>(1);
|
||||
TypeConverter typeConverter = beanFactory.getTypeConverter();
|
||||
value = beanFactory.resolveDependency(descriptor, beanName, autowiredBeanNames, typeConverter);
|
||||
synchronized (this) {
|
||||
if (!this.cached) {
|
||||
if (value != null || this.required) {
|
||||
this.cachedFieldValue = descriptor;
|
||||
registerDependentBeans(beanName, autowiredBeanNames);
|
||||
if (autowiredBeanNames.size() == 1) {
|
||||
String autowiredBeanName = autowiredBeanNames.iterator().next();
|
||||
if (beanFactory.containsBean(autowiredBeanName)) {
|
||||
if (beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {
|
||||
this.cachedFieldValue = new RuntimeBeanReference(autowiredBeanName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.cachedFieldValue = null;
|
||||
}
|
||||
this.cached = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (value != null) {
|
||||
ReflectionUtils.makeAccessible(field);
|
||||
field.set(bean, value);
|
||||
}
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
throw new BeanCreationException("Could not autowire field: " + field, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Class representing injection information about an annotated method.
|
||||
*/
|
||||
private class AutowiredMethodElement extends InjectionMetadata.InjectedElement {
|
||||
|
||||
private final boolean required;
|
||||
|
||||
private volatile boolean cached = false;
|
||||
|
||||
private volatile Object[] cachedMethodArguments;
|
||||
|
||||
public AutowiredMethodElement(Method method, boolean required, PropertyDescriptor pd) {
|
||||
super(method, pd);
|
||||
this.required = required;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable {
|
||||
if (checkPropertySkipping(pvs)) {
|
||||
return;
|
||||
}
|
||||
Method method = (Method) this.member;
|
||||
try {
|
||||
Object[] arguments;
|
||||
if (this.cached) {
|
||||
// Shortcut for avoiding synchronization...
|
||||
arguments = resolveCachedArguments(beanName);
|
||||
}
|
||||
else {
|
||||
Class<?>[] paramTypes = method.getParameterTypes();
|
||||
arguments = new Object[paramTypes.length];
|
||||
DependencyDescriptor[] descriptors = new DependencyDescriptor[paramTypes.length];
|
||||
Set<String> autowiredBeanNames = new LinkedHashSet<String>(paramTypes.length);
|
||||
TypeConverter typeConverter = beanFactory.getTypeConverter();
|
||||
for (int i = 0; i < arguments.length; i++) {
|
||||
MethodParameter methodParam = new MethodParameter(method, i);
|
||||
GenericTypeResolver.resolveParameterType(methodParam, bean.getClass());
|
||||
descriptors[i] = new DependencyDescriptor(methodParam, this.required);
|
||||
arguments[i] = beanFactory.resolveDependency(
|
||||
descriptors[i], beanName, autowiredBeanNames, typeConverter);
|
||||
if (arguments[i] == null && !this.required) {
|
||||
arguments = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
synchronized (this) {
|
||||
if (!this.cached) {
|
||||
if (arguments != null) {
|
||||
this.cachedMethodArguments = new Object[arguments.length];
|
||||
for (int i = 0; i < arguments.length; i++) {
|
||||
this.cachedMethodArguments[i] = descriptors[i];
|
||||
}
|
||||
registerDependentBeans(beanName, autowiredBeanNames);
|
||||
if (autowiredBeanNames.size() == paramTypes.length) {
|
||||
Iterator<String> it = autowiredBeanNames.iterator();
|
||||
for (int i = 0; i < paramTypes.length; i++) {
|
||||
String autowiredBeanName = it.next();
|
||||
if (beanFactory.containsBean(autowiredBeanName)) {
|
||||
if (beanFactory.isTypeMatch(autowiredBeanName, paramTypes[i])) {
|
||||
this.cachedMethodArguments[i] = new RuntimeBeanReference(autowiredBeanName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.cachedMethodArguments = null;
|
||||
}
|
||||
this.cached = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (arguments != null) {
|
||||
ReflectionUtils.makeAccessible(method);
|
||||
method.invoke(bean, arguments);
|
||||
}
|
||||
}
|
||||
catch (InvocationTargetException ex) {
|
||||
throw ex.getTargetException();
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
throw new BeanCreationException("Could not autowire method: " + method, ex);
|
||||
}
|
||||
}
|
||||
|
||||
private Object[] resolveCachedArguments(String beanName) {
|
||||
if (this.cachedMethodArguments == null) {
|
||||
return null;
|
||||
}
|
||||
Object[] arguments = new Object[this.cachedMethodArguments.length];
|
||||
for (int i = 0; i < arguments.length; i++) {
|
||||
arguments[i] = resolvedCachedArgument(beanName, this.cachedMethodArguments[i]);
|
||||
}
|
||||
return arguments;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -30,25 +30,25 @@ import org.w3c.dom.Element;
|
||||
* attribute is expected to point to an implementation of Tasklet).
|
||||
*
|
||||
* @author Michael Minella
|
||||
* @author Chris Schaefer
|
||||
* @since 3.0
|
||||
*/
|
||||
public class BatchletParser extends AbstractSingleBeanDefinitionParser {
|
||||
|
||||
private static final String REF = "ref";
|
||||
|
||||
public void parseBatchlet(Element stepElement, Element taskletElement, AbstractBeanDefinition bd,
|
||||
ParserContext parserContext) {
|
||||
|
||||
public void parseBatchlet(Element batchletElement, AbstractBeanDefinition bd, ParserContext parserContext) {
|
||||
bd.setBeanClass(StepFactoryBean.class);
|
||||
bd.setAttribute("isNamespaceStep", false);
|
||||
|
||||
String taskletRef = taskletElement.getAttribute(REF);
|
||||
String taskletRef = batchletElement.getAttribute(REF);
|
||||
|
||||
if (StringUtils.hasText(taskletRef)) {
|
||||
bd.getPropertyValues().addPropertyValue("tasklet", new RuntimeBeanReference(taskletRef));
|
||||
}
|
||||
|
||||
bd.setRole(BeanDefinition.ROLE_SUPPORT);
|
||||
bd.setSource(parserContext.extractSource(taskletElement));
|
||||
bd.setSource(parserContext.extractSource(batchletElement));
|
||||
|
||||
new PropertyParser(taskletRef, parserContext).parseProperties(batchletElement);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,11 +41,11 @@ import org.w3c.dom.NodeList;
|
||||
* {@link ItemProcessor}, and {@link ItemWriter}).
|
||||
*
|
||||
* @author Michael Minella
|
||||
* @author Chris Schaefer
|
||||
* @since 3.0
|
||||
*
|
||||
*/
|
||||
public class ChunkParser {
|
||||
|
||||
private static final String TIME_LIMIT_ATTRIBUTE = "time-limit";
|
||||
private static final String ITEM_COUNT_ATTRIBUTE = "item-count";
|
||||
private static final String CHECKPOINT_ALGORITHM_ELEMENT = "checkpoint-algorithm";
|
||||
@@ -77,8 +77,7 @@ public class ChunkParser {
|
||||
parseSimpleAttribute(element, propertyValues, ITEM_COUNT_ATTRIBUTE, "commitInterval");
|
||||
parseSimpleAttribute(element, propertyValues, TIME_LIMIT_ATTRIBUTE, "timeout");
|
||||
} else if(checkpointPolicy.equals(CUSTOM_CHECKPOINT_POLICY)) {
|
||||
parseCustomCheckpointAlgorithm(element, parserContext,
|
||||
propertyValues);
|
||||
parseCustomCheckpointAlgorithm(element, parserContext, propertyValues);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,26 +101,31 @@ public class ChunkParser {
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
private void parseChildElement(Element element,
|
||||
ParserContext parserContext, MutablePropertyValues propertyValues,
|
||||
Node nd) {
|
||||
private void parseChildElement(Element element, ParserContext parserContext,
|
||||
MutablePropertyValues propertyValues, Node nd) {
|
||||
if (nd instanceof Element) {
|
||||
Element nestedElement = (Element) nd;
|
||||
String name = nestedElement.getLocalName();
|
||||
|
||||
String artifactName = nestedElement.getAttribute(REF_ATTRIBUTE);
|
||||
|
||||
if(name.equals(READER_ELEMENT)) {
|
||||
if (StringUtils.hasText(artifactName)) {
|
||||
propertyValues.addPropertyValue("itemReader", new RuntimeBeanReference(artifactName));
|
||||
}
|
||||
|
||||
new PropertyParser(artifactName, parserContext).parseProperties(nestedElement);
|
||||
} else if(name.equals(PROCESSOR_ELEMENT)) {
|
||||
if (StringUtils.hasText(artifactName)) {
|
||||
propertyValues.addPropertyValue("itemProcessor", new RuntimeBeanReference(artifactName));
|
||||
}
|
||||
|
||||
new PropertyParser(artifactName, parserContext).parseProperties(nestedElement);
|
||||
} else if(name.equals(WRITER_ELEMENT)) {
|
||||
if (StringUtils.hasText(artifactName)) {
|
||||
propertyValues.addPropertyValue("itemWriter", new RuntimeBeanReference(artifactName));
|
||||
}
|
||||
|
||||
new PropertyParser(artifactName, parserContext).parseProperties(nestedElement);
|
||||
} else if(name.equals(SKIPPABLE_EXCEPTION_CLASSES_ELEMENT)) {
|
||||
ManagedMap exceptionClasses = new ExceptionElementParser().parse(element, parserContext, SKIPPABLE_EXCEPTION_CLASSES_ELEMENT);
|
||||
if(exceptionClasses != null) {
|
||||
@@ -146,8 +150,7 @@ public class ChunkParser {
|
||||
}
|
||||
}
|
||||
|
||||
private void parseCustomCheckpointAlgorithm(Element element,
|
||||
ParserContext parserContext, MutablePropertyValues propertyValues) {
|
||||
private void parseCustomCheckpointAlgorithm(Element element, ParserContext parserContext, MutablePropertyValues propertyValues) {
|
||||
List<Element> elements = DomUtils.getChildElementsByTagName(element, CHECKPOINT_ALGORITHM_ELEMENT);
|
||||
|
||||
if(elements.size() == 1) {
|
||||
@@ -157,6 +160,8 @@ public class ChunkParser {
|
||||
if(StringUtils.hasText(name)) {
|
||||
propertyValues.addPropertyValue("chunkCompletionPolicy", new RuntimeBeanReference(name));
|
||||
}
|
||||
|
||||
new PropertyParser(name, parserContext).parseProperties(checkpointAlgorithmElement);
|
||||
} else if(elements.size() > 1){
|
||||
parserContext.getReaderContext().error(
|
||||
"The <checkpoint-algorithm/> element may not appear more than once in a single <"
|
||||
|
||||
@@ -29,6 +29,7 @@ import org.w3c.dom.Element;
|
||||
* parses a decision element and assumes that it refers to a {@link JobExecutionDecider}
|
||||
*
|
||||
* @author Michael Minella
|
||||
* @author Chris Schaefer
|
||||
* @since 3.0
|
||||
*/
|
||||
public class DecisionParser {
|
||||
@@ -37,7 +38,6 @@ public class DecisionParser {
|
||||
private static final String REF_ATTRIBUTE = "ref";
|
||||
|
||||
public Collection<BeanDefinition> parse(Element element, ParserContext parserContext) {
|
||||
|
||||
String refAttribute = element.getAttribute(REF_ATTRIBUTE);
|
||||
String idAttribute = element.getAttribute(ID_ATTRIBUTE);
|
||||
|
||||
@@ -45,6 +45,9 @@ public class DecisionParser {
|
||||
BeanDefinitionBuilder.genericBeanDefinition("org.springframework.batch.core.job.flow.support.state.DecisionState");
|
||||
stateBuilder.addConstructorArgValue(new RuntimeBeanReference(refAttribute));
|
||||
stateBuilder.addConstructorArgValue(idAttribute);
|
||||
|
||||
new PropertyParser(refAttribute, parserContext).parseProperties(element);
|
||||
|
||||
return FlowParser.getNextElements(parserContext, stateBuilder.getBeanDefinition(), element);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,9 +17,11 @@ package org.springframework.batch.core.jsr.configuration.xml;
|
||||
|
||||
import org.springframework.batch.core.configuration.xml.CoreNamespaceUtils;
|
||||
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.AbstractSingleBeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.context.annotation.AnnotationConfigUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
@@ -28,14 +30,17 @@ import org.w3c.dom.Element;
|
||||
* the standard Spring Batch artifacts.
|
||||
*
|
||||
* @author Michael Minella
|
||||
* @author Chris Schaefer
|
||||
* @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 RESTARTABLE_ATTRIBUTE = "restartable";
|
||||
private static final String ID_ATTRIBUTE = "id";
|
||||
|
||||
@Override
|
||||
@Override
|
||||
protected Class<JobFactoryBean> getBeanClass(Element element) {
|
||||
return JobFactoryBean.class;
|
||||
}
|
||||
@@ -43,6 +48,7 @@ public class JobParser extends AbstractSingleBeanDefinitionParser {
|
||||
@Override
|
||||
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
|
||||
CoreNamespaceUtils.autoregisterBeansForNamespace(parserContext, parserContext.extractSource(element));
|
||||
autoregisterJsrBeansForNamespace(parserContext);
|
||||
|
||||
String jobName = element.getAttribute(ID_ATTRIBUTE);
|
||||
builder.addConstructorArgValue(jobName);
|
||||
@@ -56,5 +62,34 @@ public class JobParser extends AbstractSingleBeanDefinitionParser {
|
||||
builder.addPropertyValue("flow", flowDef);
|
||||
|
||||
new ListnerParser(JobListenerFactoryBean.class, "jobExecutionListeners").parseListeners(element, parserContext, builder);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,6 @@ import org.w3c.dom.Element;
|
||||
* @since 3.0
|
||||
*/
|
||||
public class ListnerParser {
|
||||
|
||||
private static final String REF_ATTRIBUTE = "ref";
|
||||
private static final String LISTENER_ELEMENT = "listener";
|
||||
private static final String LISTENERS_ELEMENT = "listeners";
|
||||
@@ -43,11 +42,11 @@ public class ListnerParser {
|
||||
private Class listenerType;
|
||||
private String propertyKey;
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
@SuppressWarnings("rawtypes")
|
||||
public ListnerParser(Class listenerType, String propertyKey) {
|
||||
this.listenerType = listenerType;
|
||||
this.propertyKey = propertyKey;
|
||||
}
|
||||
this.listenerType = listenerType;
|
||||
}
|
||||
|
||||
public void parseListeners(Element element, ParserContext parserContext, AbstractBeanDefinition bd) {
|
||||
ManagedList<AbstractBeanDefinition> listeners = parseListeners(element, parserContext);
|
||||
@@ -78,9 +77,14 @@ public class ListnerParser {
|
||||
listeners.setMergeEnabled(false);
|
||||
List<Element> listenerElements = DomUtils.getChildElementsByTagName(listenersElement, LISTENER_ELEMENT);
|
||||
for (Element listenerElement : listenerElements) {
|
||||
String beanName = listenerElement.getAttribute(REF_ATTRIBUTE);
|
||||
|
||||
BeanDefinitionBuilder bd = BeanDefinitionBuilder.genericBeanDefinition(listenerType);
|
||||
bd.addPropertyValue("delegate", new RuntimeBeanReference(listenerElement.getAttribute(REF_ATTRIBUTE)));
|
||||
bd.addPropertyValue("delegate", new RuntimeBeanReference(beanName));
|
||||
|
||||
listeners.add(bd.getBeanDefinition());
|
||||
|
||||
new PropertyParser(beanName, parserContext).parseProperties(listenerElement);
|
||||
}
|
||||
parserContext.popAndRegisterContainingComponent();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* 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 java.util.List;
|
||||
import java.util.Properties;
|
||||
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.xml.DomUtils;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Parser for the <properties /> element defined by JSR-352.
|
||||
* </p>
|
||||
*
|
||||
* @author Chris Schaefer
|
||||
* @since 3.0
|
||||
*/
|
||||
public class PropertyParser {
|
||||
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 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 String beanName;
|
||||
private ParserContext parserContext;
|
||||
|
||||
public PropertyParser(String beanName, ParserContext parserContext) {
|
||||
this.beanName = beanName;
|
||||
this.parserContext = parserContext;
|
||||
|
||||
registerBatchPropertyContext();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Parses <property> tag values from the provided {@link Element} if it contains a <properties /> element.
|
||||
* Only one <properties /> element may be present. <property> elements have a name and value attribute
|
||||
* which represent the property entries key and value.
|
||||
* </p>
|
||||
*
|
||||
* @param element
|
||||
*/
|
||||
public void parseProperties(Element element) {
|
||||
List<Element> propertiesElements = DomUtils.getChildElementsByTagName(element, PROPERTIES_ELEMENT);
|
||||
|
||||
Properties properties = new Properties();
|
||||
|
||||
if (propertiesElements.size() == 1) {
|
||||
List<Element> propertyElements = DomUtils.getChildElementsByTagName(propertiesElements.get(0), PROPERTY_ELEMENT);
|
||||
|
||||
for (Element propertyElement : propertyElements) {
|
||||
properties.put(propertyElement.getAttribute(PROPERTY_NAME_ATTRIBUTE), propertyElement.getAttribute(PROPERTY_VALUE_ATTRIBUTE));
|
||||
}
|
||||
|
||||
addProperties(properties);
|
||||
} else if (propertiesElements.size() > 1) {
|
||||
parserContext.getReaderContext().error("The <properties> element may not appear more than once in a single <listener>.", element);
|
||||
}
|
||||
}
|
||||
|
||||
private void addProperties(Properties properties) {
|
||||
BeanDefinition beanDefinition = parserContext.getRegistry().getBeanDefinition(BATCH_PROPERTY_CONTEXT_BEAN_NAME);
|
||||
|
||||
BatchPropertyContext batchPropertyContext = new BatchPropertyContext();
|
||||
BatchPropertyContext.BatchPropertyContextEntry batchPropertyContextEntry =
|
||||
batchPropertyContext.new BatchPropertyContextEntry(beanName, properties);
|
||||
|
||||
ManagedList<BatchPropertyContext.BatchPropertyContextEntry> managedList = new ManagedList<BatchPropertyContext.BatchPropertyContextEntry>();
|
||||
managedList.setMergeEnabled(true);
|
||||
managedList.add(batchPropertyContextEntry);
|
||||
|
||||
beanDefinition.getPropertyValues().addPropertyValue(BATCH_CONTEXT_ENTRIES_PROPERTY_NAME, managedList);
|
||||
}
|
||||
|
||||
private void registerBatchPropertyContext() {
|
||||
if (!parserContext.getRegistry().containsBeanDefinition(BATCH_PROPERTY_CONTEXT_BEAN_NAME)) {
|
||||
BeanDefinitionBuilder batchPropertyContextBeanDefinitionBuilder =
|
||||
BeanDefinitionBuilder.genericBeanDefinition(BATCH_PROPERTY_CONTEXT_BEAN_CLASS_NAME);
|
||||
|
||||
AbstractBeanDefinition batchPropertyContextBeanDefinition = batchPropertyContextBeanDefinitionBuilder.getBeanDefinition();
|
||||
batchPropertyContextBeanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
|
||||
parserContext.getRegistry().registerBeanDefinition(BATCH_PROPERTY_CONTEXT_BEAN_NAME, batchPropertyContextBeanDefinition);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -37,17 +37,17 @@ import org.springframework.util.StringUtils;
|
||||
*
|
||||
* @author Michael Minella
|
||||
* @author Glenn Renfro
|
||||
* @author Chris Schaefer
|
||||
* @since 3.0
|
||||
*/
|
||||
public class StepParser extends AbstractSingleBeanDefinitionParser {
|
||||
|
||||
private static final String CHUNK_ELEMENT = "chunk";
|
||||
private static final String BATCHLET_ELEMENT = "batchlet";
|
||||
private static final String ALLOW_START_IF_COMPLETE_ATTRIBUTE = "allow-start-if-complete";
|
||||
private static final String START_LIMIT_ATTRIBUTE = "start-limit";
|
||||
private static final String SPLIT_ID_ATTRIBUTE = "id";
|
||||
|
||||
protected Collection<BeanDefinition> parse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
|
||||
protected Collection<BeanDefinition> parse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
|
||||
BeanDefinitionBuilder defBuilder = BeanDefinitionBuilder.genericBeanDefinition();
|
||||
AbstractBeanDefinition bd = defBuilder.getRawBeanDefinition();
|
||||
bd.setBeanClass(StepFactoryBean.class);
|
||||
@@ -71,6 +71,7 @@ public class StepParser extends AbstractSingleBeanDefinitionParser {
|
||||
}
|
||||
|
||||
new ListnerParser(StepListenerFactoryBean.class, "listeners").parseListeners(element, parserContext, bd);
|
||||
new PropertyParser(stepName, parserContext).parseProperties(element);
|
||||
|
||||
// look at all nested elements
|
||||
NodeList children = element.getChildNodes();
|
||||
@@ -83,7 +84,7 @@ public class StepParser extends AbstractSingleBeanDefinitionParser {
|
||||
String name = nestedElement.getLocalName();
|
||||
|
||||
if(name.equalsIgnoreCase(BATCHLET_ELEMENT)) {
|
||||
new BatchletParser().parseBatchlet(element, nestedElement, bd, parserContext);
|
||||
new BatchletParser().parseBatchlet(nestedElement, bd, parserContext);
|
||||
} else if(name.equals(CHUNK_ELEMENT)) {
|
||||
new ChunkParser().parse(nestedElement, bd, parserContext);
|
||||
}
|
||||
|
||||
@@ -45,16 +45,14 @@ import org.springframework.batch.core.JobParameters;
|
||||
import org.springframework.batch.core.converter.JobParametersConverter;
|
||||
import org.springframework.batch.core.explore.JobExplorer;
|
||||
import org.springframework.batch.core.jsr.JobContext;
|
||||
import org.springframework.batch.core.jsr.configuration.support.BatchPropertyContext;
|
||||
import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException;
|
||||
import org.springframework.batch.core.repository.JobRepository;
|
||||
import org.springframework.beans.factory.access.BeanFactoryLocator;
|
||||
import org.springframework.beans.factory.access.BeanFactoryReference;
|
||||
import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
|
||||
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.GenericBeanDefinition;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.access.ContextSingletonBeanFactoryLocator;
|
||||
import org.springframework.context.support.GenericXmlApplicationContext;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
@@ -114,9 +112,11 @@ import org.springframework.util.Assert;
|
||||
* how job instances are identified differently.
|
||||
*
|
||||
* @author Michael Minella
|
||||
* @author Chris Schaefer
|
||||
* @since 3.0
|
||||
*/
|
||||
public class JsrJobOperator implements JobOperator {
|
||||
private static final String BATCH_PROPERTY_CONTEXT_BEAN_NAME = "batchPropertyContext";
|
||||
|
||||
private org.springframework.batch.core.launch.JobOperator batchJobOperator;
|
||||
private JobExplorer jobExplorer;
|
||||
@@ -406,10 +406,8 @@ public class JsrJobOperator implements JobOperator {
|
||||
}
|
||||
|
||||
batchContext.setParent(baseContext);
|
||||
GenericBeanDefinition bd = new GenericBeanDefinition();
|
||||
bd.setBeanClass(AutowiredAnnotationBeanPostProcessor.class);
|
||||
batchContext.registerBeanDefinition("postProcessor", bd);
|
||||
batchContext.refresh();
|
||||
|
||||
final Job job = batchContext.getBean(Job.class);
|
||||
|
||||
if(!job.isRestartable()) {
|
||||
@@ -426,8 +424,12 @@ public class JsrJobOperator implements JobOperator {
|
||||
}
|
||||
|
||||
try {
|
||||
ConfigurableListableBeanFactory factory = ((ConfigurableApplicationContext)batchContext).getBeanFactory();
|
||||
factory.registerSingleton(job.getName() + "_" + jobExecution.getId() + "_jobContext", new JobContext(jobExecution, jobParametersConverter));
|
||||
ConfigurableListableBeanFactory factory = batchContext.getBeanFactory();
|
||||
|
||||
BatchPropertyContext batchPropertyContext = factory.getBean(BATCH_PROPERTY_CONTEXT_BEAN_NAME, BatchPropertyContext.class);
|
||||
Properties properties = batchPropertyContext.getBatchProperties("job-" + job.getName());
|
||||
|
||||
factory.registerSingleton(job.getName() + "_" + jobExecution.getId() + "_jobContext", new JobContext(jobExecution, properties));
|
||||
|
||||
taskExecutor.execute(new Runnable() {
|
||||
|
||||
@@ -485,9 +487,6 @@ public class JsrJobOperator implements JobOperator {
|
||||
}
|
||||
|
||||
batchContext.setParent(baseContext);
|
||||
GenericBeanDefinition bd = new GenericBeanDefinition();
|
||||
bd.setBeanClass(AutowiredAnnotationBeanPostProcessor.class);
|
||||
batchContext.registerBeanDefinition("postProcessor", bd);
|
||||
batchContext.refresh();
|
||||
final Job job = batchContext.getBean(Job.class);
|
||||
|
||||
@@ -504,8 +503,12 @@ public class JsrJobOperator implements JobOperator {
|
||||
}
|
||||
|
||||
try {
|
||||
ConfigurableListableBeanFactory factory = ((ConfigurableApplicationContext)batchContext).getBeanFactory();
|
||||
factory.registerSingleton(job.getName() + "_" + jobExecution.getId() + "_jobContext", new JobContext(jobExecution, jobParametersConverter));
|
||||
ConfigurableListableBeanFactory factory = batchContext.getBeanFactory();
|
||||
|
||||
BatchPropertyContext batchPropertyContext = factory.getBean(BATCH_PROPERTY_CONTEXT_BEAN_NAME, BatchPropertyContext.class);
|
||||
Properties properties = batchPropertyContext.getBatchProperties("job-" + job.getName());
|
||||
|
||||
factory.registerSingleton(job.getName() + "_" + jobExecution.getId() + "_jobContext", new JobContext(jobExecution, properties));
|
||||
|
||||
taskExecutor.execute(new Runnable() {
|
||||
|
||||
|
||||
@@ -1,3 +1,18 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
@@ -16,7 +31,6 @@ import org.springframework.batch.core.JobExecution;
|
||||
import org.springframework.batch.core.JobInstance;
|
||||
import org.springframework.batch.core.JobParameters;
|
||||
import org.springframework.batch.core.JobParametersBuilder;
|
||||
import org.springframework.batch.core.converter.JobParametersConverter;
|
||||
|
||||
public class JobContextTests {
|
||||
|
||||
@@ -25,13 +39,15 @@ public class JobContextTests {
|
||||
private JobExecution execution;
|
||||
@Mock
|
||||
private JobInstance instance;
|
||||
@Mock
|
||||
private JobParametersConverter converter;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
context = new JobContext(execution, converter);
|
||||
|
||||
Properties properties = new Properties();
|
||||
properties.put("jobLevelProperty1", "jobLevelValue1");
|
||||
|
||||
context = new JobContext(execution, properties);
|
||||
when(execution.getJobInstance()).thenReturn(instance);
|
||||
}
|
||||
|
||||
@@ -68,19 +84,19 @@ public class JobContextTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetProperties() {
|
||||
public void testJobParameters() {
|
||||
JobParameters params = new JobParametersBuilder()
|
||||
.addString("key1", "value1")
|
||||
.toJobParameters();
|
||||
Properties results = new Properties();
|
||||
results.put("key1", "value1");
|
||||
|
||||
when(execution.getJobParameters()).thenReturn(params);
|
||||
when(converter.getProperties(params)).thenReturn(results);
|
||||
|
||||
Properties props = context.getProperties();
|
||||
assertEquals("value1", execution.getJobParameters().getString("key1"));
|
||||
}
|
||||
|
||||
assertEquals("value1", props.get("key1"));
|
||||
@Test
|
||||
public void testJobProperties() {
|
||||
assertEquals("jobLevelValue1", context.getProperties().get("jobLevelProperty1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -1,3 +1,18 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
@@ -16,7 +31,6 @@ import org.springframework.batch.core.ExitStatus;
|
||||
import org.springframework.batch.core.JobExecution;
|
||||
import org.springframework.batch.core.JobParametersBuilder;
|
||||
import org.springframework.batch.core.StepExecution;
|
||||
import org.springframework.batch.core.converter.JobParametersConverterSupport;
|
||||
import org.springframework.batch.item.ExecutionContext;
|
||||
|
||||
public class StepContextTests {
|
||||
@@ -44,7 +58,10 @@ public class StepContextTests {
|
||||
executionContext = new ExecutionContext();
|
||||
stepExecution.setExecutionContext(executionContext);
|
||||
|
||||
stepContext = new StepContext(stepExecution, new JobParametersConverterSupport());
|
||||
Properties properties = new Properties();
|
||||
properties.put("key", "value");
|
||||
|
||||
stepContext = new StepContext(stepExecution, properties);
|
||||
stepContext.setTransientUserData("This is my transient data");
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,558 @@
|
||||
/*
|
||||
* 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 java.io.Serializable;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import javax.batch.api.BatchProperty;
|
||||
import javax.batch.api.Batchlet;
|
||||
import javax.batch.api.chunk.ItemProcessor;
|
||||
import javax.batch.api.chunk.ItemReader;
|
||||
import javax.batch.api.chunk.ItemWriter;
|
||||
import javax.inject.Inject;
|
||||
import junit.framework.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.aop.framework.Advised;
|
||||
import org.springframework.batch.core.ExitStatus;
|
||||
import org.springframework.batch.core.Job;
|
||||
import org.springframework.batch.core.JobExecution;
|
||||
import org.springframework.batch.core.JobParameters;
|
||||
import org.springframework.batch.core.StepExecution;
|
||||
import org.springframework.batch.core.job.flow.FlowExecutionStatus;
|
||||
import org.springframework.batch.core.job.flow.JobExecutionDecider;
|
||||
import org.springframework.batch.core.jsr.StepContext;
|
||||
import org.springframework.batch.core.launch.JobLauncher;
|
||||
import org.springframework.batch.core.scope.context.StepSynchronizationManager;
|
||||
import org.springframework.batch.repeat.CompletionPolicy;
|
||||
import org.springframework.batch.repeat.RepeatContext;
|
||||
import org.springframework.batch.repeat.RepeatStatus;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Test cases for parsing various <properties /> elements defined by JSR-352.
|
||||
* </p>
|
||||
*
|
||||
* @author Chris Schaefer
|
||||
*/
|
||||
@ContextConfiguration
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
public class JobPropertyTests {
|
||||
@Autowired
|
||||
private TestItemReader testItemReader;
|
||||
|
||||
@Autowired
|
||||
private Job job;
|
||||
|
||||
@Autowired
|
||||
private JobLauncher jobLauncher;
|
||||
|
||||
@Autowired
|
||||
private TestItemProcessor testItemProcessor;
|
||||
|
||||
@Autowired
|
||||
private TestItemWriter testItemWriter;
|
||||
|
||||
@Autowired
|
||||
private TestCheckpointAlgorithm testCheckpointAlgorithm;
|
||||
|
||||
@Autowired
|
||||
private TestDecider testDecider;
|
||||
|
||||
@Autowired
|
||||
private TestStepListener testStepListener;
|
||||
|
||||
@Autowired
|
||||
private TestBatchlet testBatchlet;
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
@Test
|
||||
public void testJobLevelPropertiesInItemReader() throws Exception {
|
||||
assertEquals("jobPropertyValue1", testItemReader.getJobPropertyName1());
|
||||
assertEquals("jobPropertyValue2", testItemReader.getJobPropertyName2());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStepContextProperties() throws Exception {
|
||||
JobExecution jobExecution = jobLauncher.run(job, new JobParameters());
|
||||
|
||||
Properties step1Properties = new Properties();
|
||||
Properties step2Properties = new Properties();
|
||||
|
||||
for (StepExecution stepExecution : jobExecution.getStepExecutions()) {
|
||||
try {
|
||||
StepSynchronizationManager.register(stepExecution);
|
||||
String contextBeanName = stepExecution.getStepName() + "stepContext";
|
||||
|
||||
// fix me? StepSynchronizationManager.context returns org.springframework.batch.core.scope.context.StepContext
|
||||
StepContext stepContext = (StepContext) ((Advised)applicationContext.getBean(contextBeanName)).getTargetSource().getTarget();
|
||||
|
||||
if(contextBeanName.startsWith("step1")) {
|
||||
step1Properties.putAll(stepContext.getProperties());
|
||||
} else {
|
||||
step2Properties.putAll(stepContext.getProperties());
|
||||
}
|
||||
} finally {
|
||||
StepSynchronizationManager.close();
|
||||
}
|
||||
}
|
||||
|
||||
assertEquals(4, step1Properties.size());
|
||||
assertEquals("step1PropertyValue1", step1Properties.getProperty("step1PropertyName1"));
|
||||
assertEquals("step1PropertyValue2", step1Properties.getProperty("step1PropertyName2"));
|
||||
assertEquals("jobPropertyValue1", step1Properties.getProperty("jobPropertyName1"));
|
||||
assertEquals("jobPropertyValue2", step1Properties.getProperty("jobPropertyName2"));
|
||||
|
||||
assertEquals(4, step2Properties.size());
|
||||
assertEquals("step2PropertyValue1", step2Properties.getProperty("step2PropertyName1"));
|
||||
assertEquals("step2PropertyValue2", step2Properties.getProperty("step2PropertyName2"));
|
||||
assertEquals("jobPropertyValue1", step2Properties.getProperty("jobPropertyName1"));
|
||||
assertEquals("jobPropertyValue2", step2Properties.getProperty("jobPropertyName2"));
|
||||
|
||||
assertEquals(ExitStatus.COMPLETED, jobExecution.getExitStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testItemReaderProperties() throws Exception {
|
||||
assertEquals("readerPropertyValue1", testItemReader.getReaderPropertyName1());
|
||||
assertEquals("readerPropertyValue2", testItemReader.getReaderPropertyName2());
|
||||
assertEquals("annotationNamedReaderPropertyValue", testItemReader.getAnnotationNamedProperty());
|
||||
assertNull(testItemReader.getNotDefinedProperty());
|
||||
assertNull(testItemReader.getNotDefinedAnnotationNamedProperty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testItemProcessorProperties() throws Exception {
|
||||
Assert.assertEquals("processorPropertyValue1", testItemProcessor.getProcessorPropertyName1());
|
||||
Assert.assertEquals("processorPropertyValue2", testItemProcessor.getProcessorPropertyName2());
|
||||
assertEquals("annotationNamedProcessorPropertyValue", testItemProcessor.getAnnotationNamedProperty());
|
||||
assertNull(testItemProcessor.getNotDefinedProperty());
|
||||
assertNull(testItemProcessor.getNotDefinedAnnotationNamedProperty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testItemWriterProperties() throws Exception {
|
||||
Assert.assertEquals("writerPropertyValue1", testItemWriter.getWriterPropertyName1());
|
||||
Assert.assertEquals("writerPropertyValue2", testItemWriter.getWriterPropertyName2());
|
||||
assertEquals("annotationNamedWriterPropertyValue", testItemWriter.getAnnotationNamedProperty());
|
||||
assertNull(testItemWriter.getNotDefinedProperty());
|
||||
assertNull(testItemWriter.getNotDefinedAnnotationNamedProperty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCheckpointAlgorithmProperties() throws Exception {
|
||||
Assert.assertEquals("algorithmPropertyValue1", testCheckpointAlgorithm.getAlgorithmPropertyName1());
|
||||
Assert.assertEquals("algorithmPropertyValue2", testCheckpointAlgorithm.getAlgorithmPropertyName2());
|
||||
assertEquals("annotationNamedAlgorithmPropertyValue", testCheckpointAlgorithm.getAnnotationNamedProperty());
|
||||
assertNull(testCheckpointAlgorithm.getNotDefinedProperty());
|
||||
assertNull(testCheckpointAlgorithm.getNotDefinedAnnotationNamedProperty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeciderProperties() throws Exception {
|
||||
Assert.assertEquals("deciderPropertyValue1", testDecider.getDeciderPropertyName1());
|
||||
Assert.assertEquals("deciderPropertyValue2", testDecider.getDeciderPropertyName2());
|
||||
assertEquals("annotationNamedDeciderPropertyValue", testDecider.getAnnotationNamedProperty());
|
||||
assertNull(testDecider.getNotDefinedProperty());
|
||||
assertNull(testDecider.getNotDefinedAnnotationNamedProperty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStepListenerProperties() throws Exception {
|
||||
Assert.assertEquals("stepListenerPropertyValue1", testStepListener.getStepListenerPropertyName1());
|
||||
Assert.assertEquals("stepListenerPropertyValue2", testStepListener.getStepListenerPropertyName2());
|
||||
assertEquals("annotationNamedStepListenerPropertyValue", testStepListener.getAnnotationNamedProperty());
|
||||
assertNull(testStepListener.getNotDefinedProperty());
|
||||
assertNull(testStepListener.getNotDefinedAnnotationNamedProperty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBatchletProperties() throws Exception {
|
||||
Assert.assertEquals("batchletPropertyValue1", testBatchlet.getBatchletPropertyName1());
|
||||
Assert.assertEquals("batchletPropertyValue2", testBatchlet.getBatchletPropertyName2());
|
||||
assertEquals("annotationNamedBatchletPropertyValue", testBatchlet.getAnnotationNamedProperty());
|
||||
assertNull(testBatchlet.getNotDefinedProperty());
|
||||
assertNull(testBatchlet.getNotDefinedAnnotationNamedProperty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFieldWithInjectAnnotationOnlyInjects() throws Exception {
|
||||
assertNotNull(testItemReader.getInjectAnnotatedOnlyField());
|
||||
assertEquals("Chris", testItemReader.getInjectAnnotatedOnlyField().getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFieldWithBatchPropertyAnnotationOnlyNoInjection() throws Exception {
|
||||
assertNull(testItemReader.getBatchAnnotatedOnlyField());
|
||||
}
|
||||
|
||||
public static final class TestItemReader implements ItemReader {
|
||||
private int cnt;
|
||||
|
||||
@Inject @BatchProperty String readerPropertyName1;
|
||||
@Inject @BatchProperty String readerPropertyName2;
|
||||
@Inject @BatchProperty(name = "annotationNamedReaderPropertyName") String annotationNamedProperty;
|
||||
@Inject @BatchProperty String notDefinedProperty;
|
||||
@Inject @BatchProperty(name = "notDefinedAnnotationNamedProperty") String notDefinedAnnotationNamedProperty;
|
||||
@Inject @BatchProperty String jobPropertyName1;
|
||||
@Inject @BatchProperty String jobPropertyName2;
|
||||
@Inject InjectTestObj injectAnnotatedOnlyField;
|
||||
@BatchProperty String batchAnnotatedOnlyField;
|
||||
|
||||
@Override
|
||||
public void open(Serializable serializable) throws Exception {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws Exception {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object readItem() throws Exception {
|
||||
if (cnt == 0) {
|
||||
cnt++;
|
||||
return "blah";
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Serializable checkpointInfo() throws Exception {
|
||||
return null;
|
||||
}
|
||||
|
||||
String getReaderPropertyName1() {
|
||||
return readerPropertyName1;
|
||||
}
|
||||
|
||||
String getReaderPropertyName2() {
|
||||
return readerPropertyName2;
|
||||
}
|
||||
|
||||
String getAnnotationNamedProperty() {
|
||||
return annotationNamedProperty;
|
||||
}
|
||||
|
||||
String getNotDefinedProperty() {
|
||||
return notDefinedProperty;
|
||||
}
|
||||
|
||||
String getNotDefinedAnnotationNamedProperty() {
|
||||
return notDefinedAnnotationNamedProperty;
|
||||
}
|
||||
|
||||
String getJobPropertyName1() {
|
||||
return jobPropertyName1;
|
||||
}
|
||||
|
||||
String getJobPropertyName2() {
|
||||
return jobPropertyName2;
|
||||
}
|
||||
|
||||
InjectTestObj getInjectAnnotatedOnlyField() {
|
||||
return injectAnnotatedOnlyField;
|
||||
}
|
||||
|
||||
String getBatchAnnotatedOnlyField() {
|
||||
return batchAnnotatedOnlyField;
|
||||
}
|
||||
}
|
||||
|
||||
public static final class TestItemProcessor implements ItemProcessor {
|
||||
@Inject @BatchProperty String processorPropertyName1;
|
||||
@Inject @BatchProperty String processorPropertyName2;
|
||||
@Inject @BatchProperty(name = "annotationNamedProcessorPropertyName") String annotationNamedProperty;
|
||||
@Inject @BatchProperty String notDefinedProperty;
|
||||
@Inject @BatchProperty(name = "notDefinedAnnotationNamedProperty") String notDefinedAnnotationNamedProperty;
|
||||
|
||||
@Override
|
||||
public Object processItem(Object o) throws Exception {
|
||||
return o;
|
||||
}
|
||||
|
||||
String getProcessorPropertyName1() {
|
||||
return processorPropertyName1;
|
||||
}
|
||||
|
||||
String getProcessorPropertyName2() {
|
||||
return processorPropertyName2;
|
||||
}
|
||||
|
||||
String getAnnotationNamedProperty() {
|
||||
return annotationNamedProperty;
|
||||
}
|
||||
|
||||
String getNotDefinedProperty() {
|
||||
return notDefinedProperty;
|
||||
}
|
||||
|
||||
String getNotDefinedAnnotationNamedProperty() {
|
||||
return notDefinedAnnotationNamedProperty;
|
||||
}
|
||||
}
|
||||
|
||||
public static final class TestItemWriter implements ItemWriter {
|
||||
@Inject @BatchProperty String writerPropertyName1;
|
||||
@Inject @BatchProperty String writerPropertyName2;
|
||||
@Inject @BatchProperty(name = "annotationNamedWriterPropertyName") String annotationNamedProperty;
|
||||
@Inject @BatchProperty String notDefinedProperty;
|
||||
@Inject @BatchProperty(name = "notDefinedAnnotationNamedProperty") String notDefinedAnnotationNamedProperty;
|
||||
|
||||
@Override
|
||||
public void open(Serializable serializable) throws Exception {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws Exception {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeItems(List<Object> objects) throws Exception {
|
||||
System.out.println(objects);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Serializable checkpointInfo() throws Exception {
|
||||
return null;
|
||||
}
|
||||
|
||||
String getWriterPropertyName1() {
|
||||
return writerPropertyName1;
|
||||
}
|
||||
|
||||
String getWriterPropertyName2() {
|
||||
return writerPropertyName2;
|
||||
}
|
||||
|
||||
String getAnnotationNamedProperty() {
|
||||
return annotationNamedProperty;
|
||||
}
|
||||
|
||||
String getNotDefinedProperty() {
|
||||
return notDefinedProperty;
|
||||
}
|
||||
|
||||
String getNotDefinedAnnotationNamedProperty() {
|
||||
return notDefinedAnnotationNamedProperty;
|
||||
}
|
||||
}
|
||||
|
||||
public static final class TestCheckpointAlgorithm implements CompletionPolicy {
|
||||
@Inject @BatchProperty String algorithmPropertyName1;
|
||||
@Inject @BatchProperty String algorithmPropertyName2;
|
||||
@Inject @BatchProperty(name = "annotationNamedAlgorithmPropertyName") String annotationNamedProperty;
|
||||
@Inject @BatchProperty String notDefinedProperty;
|
||||
@Inject @BatchProperty(name = "notDefinedAnnotationNamedProperty") String notDefinedAnnotationNamedProperty;
|
||||
|
||||
@Override
|
||||
public boolean isComplete(RepeatContext context, RepeatStatus result) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isComplete(RepeatContext context) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RepeatContext start(RepeatContext parent) {
|
||||
return parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(RepeatContext context) {
|
||||
}
|
||||
|
||||
String getAlgorithmPropertyName1() {
|
||||
return algorithmPropertyName1;
|
||||
}
|
||||
|
||||
String getAlgorithmPropertyName2() {
|
||||
return algorithmPropertyName2;
|
||||
}
|
||||
|
||||
String getAnnotationNamedProperty() {
|
||||
return annotationNamedProperty;
|
||||
}
|
||||
|
||||
String getNotDefinedProperty() {
|
||||
return notDefinedProperty;
|
||||
}
|
||||
|
||||
String getNotDefinedAnnotationNamedProperty() {
|
||||
return notDefinedAnnotationNamedProperty;
|
||||
}
|
||||
}
|
||||
|
||||
public static class TestDecider implements JobExecutionDecider {
|
||||
@Inject @BatchProperty String deciderPropertyName1;
|
||||
@Inject @BatchProperty String deciderPropertyName2;
|
||||
@Inject @BatchProperty(name = "annotationNamedDeciderPropertyName") String annotationNamedProperty;
|
||||
@Inject @BatchProperty String notDefinedProperty;
|
||||
@Inject @BatchProperty(name = "notDefinedAnnotationNamedProperty") String notDefinedAnnotationNamedProperty;
|
||||
|
||||
@Override
|
||||
public FlowExecutionStatus decide(JobExecution jobExecution,
|
||||
StepExecution stepExecution) {
|
||||
return new FlowExecutionStatus("step2");
|
||||
}
|
||||
|
||||
String getDeciderPropertyName1() {
|
||||
return deciderPropertyName1;
|
||||
}
|
||||
|
||||
String getDeciderPropertyName2() {
|
||||
return deciderPropertyName2;
|
||||
}
|
||||
|
||||
String getAnnotationNamedProperty() {
|
||||
return annotationNamedProperty;
|
||||
}
|
||||
|
||||
String getNotDefinedProperty() {
|
||||
return notDefinedProperty;
|
||||
}
|
||||
|
||||
String getNotDefinedAnnotationNamedProperty() {
|
||||
return notDefinedAnnotationNamedProperty;
|
||||
}
|
||||
}
|
||||
|
||||
public static class TestStepListener implements javax.batch.api.chunk.listener.ItemReadListener,
|
||||
javax.batch.api.chunk.listener.ItemProcessListener, javax.batch.api.chunk.listener.ItemWriteListener {
|
||||
@Inject @BatchProperty String stepListenerPropertyName1;
|
||||
@Inject @BatchProperty String stepListenerPropertyName2;
|
||||
@Inject @BatchProperty(name = "annotationNamedStepListenerPropertyName") String annotationNamedProperty;
|
||||
@Inject @BatchProperty String notDefinedProperty;
|
||||
@Inject @BatchProperty(name = "notDefinedAnnotationNamedProperty") String notDefinedAnnotationNamedProperty;
|
||||
|
||||
@Override
|
||||
public void beforeProcess(Object o) throws Exception {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterProcess(Object o, Object o2) throws Exception {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProcessError(Object o, Exception e) throws Exception {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeRead() throws Exception {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterRead(Object o) throws Exception {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReadError(Exception e) throws Exception {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeWrite(List<Object> objects) throws Exception {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterWrite(List<Object> objects) throws Exception {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWriteError(List<Object> objects, Exception e) throws Exception {
|
||||
}
|
||||
|
||||
String getStepListenerPropertyName1() {
|
||||
return stepListenerPropertyName1;
|
||||
}
|
||||
|
||||
String getStepListenerPropertyName2() {
|
||||
return stepListenerPropertyName2;
|
||||
}
|
||||
|
||||
String getAnnotationNamedProperty() {
|
||||
return annotationNamedProperty;
|
||||
}
|
||||
|
||||
String getNotDefinedProperty() {
|
||||
return notDefinedProperty;
|
||||
}
|
||||
|
||||
String getNotDefinedAnnotationNamedProperty() {
|
||||
return notDefinedAnnotationNamedProperty;
|
||||
}
|
||||
}
|
||||
|
||||
public static class TestBatchlet implements Batchlet {
|
||||
@Inject @BatchProperty String batchletPropertyName1;
|
||||
@Inject @BatchProperty String batchletPropertyName2;
|
||||
@Inject @BatchProperty(name = "annotationNamedBatchletPropertyName") String annotationNamedProperty;
|
||||
@Inject @BatchProperty String notDefinedProperty;
|
||||
@Inject @BatchProperty(name = "notDefinedAnnotationNamedProperty") String notDefinedAnnotationNamedProperty;
|
||||
|
||||
@Override
|
||||
public String process() throws Exception {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() throws Exception {
|
||||
}
|
||||
|
||||
String getBatchletPropertyName1() {
|
||||
return batchletPropertyName1;
|
||||
}
|
||||
|
||||
String getBatchletPropertyName2() {
|
||||
return batchletPropertyName2;
|
||||
}
|
||||
|
||||
String getAnnotationNamedProperty() {
|
||||
return annotationNamedProperty;
|
||||
}
|
||||
|
||||
String getNotDefinedProperty() {
|
||||
return notDefinedProperty;
|
||||
}
|
||||
|
||||
String getNotDefinedAnnotationNamedProperty() {
|
||||
return notDefinedAnnotationNamedProperty;
|
||||
}
|
||||
}
|
||||
|
||||
public static class InjectTestObj {
|
||||
private String name;
|
||||
|
||||
public InjectTestObj(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:c="http://www.springframework.org/schema/c"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/jobXML_1_0.xsd">
|
||||
|
||||
<!-- Not implemented (partition relatated): JSR 8.2.6.2, 8.2.6.3.1, 8.2.6.4.1, 8.2.6.5.1, 8.2.6.6.1 -->
|
||||
|
||||
<job id="job1" xmlns="http://xmlns.jcp.org/xml/ns/javaee" version="1.0">
|
||||
<!-- JSR Section 8.1.3 -->
|
||||
<properties>
|
||||
<property name="jobPropertyName1" value="jobPropertyValue1"/>
|
||||
<property name="jobPropertyName2" value="jobPropertyValue2"/>
|
||||
</properties>
|
||||
|
||||
<step id="step1">
|
||||
<!-- JSR Section 8.2.3 -->
|
||||
<properties>
|
||||
<property name="step1PropertyName1" value="step1PropertyValue1"/>
|
||||
<property name="step1PropertyName2" value="step1PropertyValue2"/>
|
||||
</properties>
|
||||
<chunk checkpoint-policy="custom">
|
||||
<reader ref="testReader">
|
||||
<!-- JSR Section 8.2.1.1.1 -->
|
||||
<properties>
|
||||
<property name="readerPropertyName1" value="readerPropertyValue1"/>
|
||||
<property name="readerPropertyName2" value="readerPropertyValue2"/>
|
||||
<property name="annotationNamedReaderPropertyName" value="annotationNamedReaderPropertyValue"/>
|
||||
<property name="nonexistentReaderPropertyName" value="nonexistentReaderPropertyValue"/>
|
||||
</properties>
|
||||
</reader>
|
||||
<processor ref="testProcessor">
|
||||
<!-- JSR Section 8.2.1.2.1 -->
|
||||
<properties>
|
||||
<property name="processorPropertyName1" value="processorPropertyValue1"/>
|
||||
<property name="processorPropertyName2" value="processorPropertyValue2"/>
|
||||
<property name="annotationNamedProcessorPropertyName" value="annotationNamedProcessorPropertyValue"/>
|
||||
<property name="nonexistentProcessorPropertyName" value="nonexistentProcessorPropertyValue"/>
|
||||
</properties>
|
||||
</processor>
|
||||
<writer ref="testWriter">
|
||||
<!-- JSR Section 8.2.1.3.1 -->
|
||||
<properties>
|
||||
<property name="writerPropertyName1" value="writerPropertyValue1"/>
|
||||
<property name="writerPropertyName2" value="writerPropertyValue2"/>
|
||||
<property name="annotationNamedWriterPropertyName" value="annotationNamedWriterPropertyValue"/>
|
||||
<property name="nonexistentWriterPropertyName" value="nonexistentWriterPropertyValue"/>
|
||||
</properties>
|
||||
</writer>
|
||||
<checkpoint-algorithm ref="testCheckpointAlgorithm">
|
||||
<!-- JSR Section 8.2.1.5.1 -->
|
||||
<properties>
|
||||
<property name="algorithmPropertyName1" value="algorithmPropertyValue1"/>
|
||||
<property name="algorithmPropertyName2" value="algorithmPropertyValue2"/>
|
||||
<property name="annotationNamedAlgorithmPropertyName" value="annotationNamedAlgorithmPropertyValue"/>
|
||||
<property name="nonexistentAlgorithmPropertyName" value="nonexistentAlgorithmPropertyValue"/>
|
||||
</properties>
|
||||
</checkpoint-algorithm>
|
||||
</chunk>
|
||||
<next on="*" to="stepDecider"/>
|
||||
</step>
|
||||
<decision id="stepDecider" ref="testDecider">
|
||||
<properties>
|
||||
<property name="deciderPropertyName1" value="deciderPropertyValue1"/>
|
||||
<property name="deciderPropertyName2" value="deciderPropertyValue2"/>
|
||||
<property name="annotationNamedDeciderPropertyName" value="annotationNamedDeciderPropertyValue"/>
|
||||
<property name="nonexistentDeciderPropertyName" value="nonexistentDeciderPropertyValue"/>
|
||||
</properties>
|
||||
<next on="*" to="step2"/>
|
||||
</decision>
|
||||
<step id="step2">
|
||||
<!-- JSR Section 8.2.3 -->
|
||||
<properties>
|
||||
<property name="step2PropertyName1" value="step2PropertyValue1"/>
|
||||
<property name="step2PropertyName2" value="step2PropertyValue2"/>
|
||||
</properties>
|
||||
<!-- JSR Section 8.2.4.1 -->
|
||||
<listeners>
|
||||
<listener ref="testStepListener">
|
||||
<properties>
|
||||
<property name="stepListenerPropertyName1" value="stepListenerPropertyValue1"/>
|
||||
<property name="stepListenerPropertyName2" value="stepListenerPropertyValue2"/>
|
||||
<property name="annotationNamedStepListenerPropertyName" value="annotationNamedStepListenerPropertyValue"/>
|
||||
<property name="nonexistentStepListenerPropertyName" value="nonexistentStepListenerPropertyValue"/>
|
||||
</properties>
|
||||
</listener>
|
||||
</listeners>
|
||||
<batchlet ref="testBatchlet">
|
||||
<!-- JSR Section 8.2.2.2 -->
|
||||
<properties>
|
||||
<property name="batchletPropertyName1" value="batchletPropertyValue1"/>
|
||||
<property name="batchletPropertyName2" value="batchletPropertyValue2"/>
|
||||
<property name="annotationNamedBatchletPropertyName" value="annotationNamedBatchletPropertyValue"/>
|
||||
<property name="nonexistentBatchletPropertyName" value="nonexistentBatchletPropertyValue"/>
|
||||
</properties>
|
||||
</batchlet>
|
||||
</step>
|
||||
</job>
|
||||
|
||||
<bean id="transactionManager" class="org.springframework.batch.support.transaction.ResourcelessTransactionManager"/>
|
||||
|
||||
<bean id="jobLauncher" class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
|
||||
<property name="jobRepository" ref="jobRepository"/>
|
||||
</bean>
|
||||
|
||||
<bean id="jobRepository" class="org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean"/>
|
||||
|
||||
<bean id="testReader" class="org.springframework.batch.core.jsr.configuration.xml.JobPropertyTests.TestItemReader"/>
|
||||
|
||||
<bean id="testProcessor" class="org.springframework.batch.core.jsr.configuration.xml.JobPropertyTests.TestItemProcessor"/>
|
||||
|
||||
<bean id="testWriter" class="org.springframework.batch.core.jsr.configuration.xml.JobPropertyTests.TestItemWriter"/>
|
||||
|
||||
<bean id="testCheckpointAlgorithm" class="org.springframework.batch.core.jsr.configuration.xml.JobPropertyTests.TestCheckpointAlgorithm"/>
|
||||
|
||||
<bean id="testBatchlet" class="org.springframework.batch.core.jsr.configuration.xml.JobPropertyTests.TestBatchlet"/>
|
||||
|
||||
<bean id="testStepListener" class="org.springframework.batch.core.jsr.configuration.xml.JobPropertyTests.TestStepListener"/>
|
||||
|
||||
<bean id="testDecider" class="org.springframework.batch.core.jsr.configuration.xml.JobPropertyTests.TestDecider"/>
|
||||
|
||||
<bean id="injectTestObj" class="org.springframework.batch.core.jsr.configuration.xml.JobPropertyTests.InjectTestObj"
|
||||
c:name="Chris"/>
|
||||
</beans>
|
||||
Reference in New Issue
Block a user