OPEN - issue BATCH-295: JobLauncher should have only one run method.

http://jira.springframework.org/browse/BATCH-295

Add a property editor for JobIdentifier so the JMX demo can work.
This commit is contained in:
dsyer
2008-01-21 11:37:12 +00:00
parent c4e47ffab0
commit badcf74c7b
11 changed files with 410 additions and 145 deletions

View File

@@ -18,38 +18,29 @@ package org.springframework.batch.execution.bootstrap.support;
import java.util.Properties;
import org.springframework.batch.core.domain.JobExecution;
import org.springframework.batch.core.domain.JobIdentifier;
import org.springframework.batch.execution.launch.JobLauncher;
import org.springframework.batch.repeat.ExitStatus;
/**
* Interface to expose for remote management of jobs. Similar to
* {@link JobLauncher}, but replaces {@link ExitStatus} with String in return
* types, so it can be inspected by remote clients like the jconsole from the
* JRE without any links to Spring Batch.
* {@link JobLauncher}, but replaces {@link JobExecution} and
* {@link JobIdentifier} with Strings in return types and method parameters, so
* it can be inspected by remote clients like the jconsole from the JRE without
* any links to Spring Batch.
*
* @author Dave Syer
*
*/
public interface ExportedJobLauncher {
/**
* Launch a job and get back a representation of the {@link ExitStatus}
* returned by a {@link JobLauncher}. Normally the launch will be
* asynchronous, so the possible values of the return type are constrained
* (it will never be {@link ExitStatus#CONTINUABLE}).
*
* @return a representation of the {@link ExitStatus} returned by a
* {@link JobLauncher}.
*/
String run();
/**
* Launch a job with the given name.
*
* @param name the name of the job to launch
* @return a representation of the {@link ExitStatus} returned by a
* {@link JobLauncher}.
*
* @return a representation of the {@link JobExecution} returned by a
* {@link JobLauncher}.
*
* @see #run()
*/
String run(String name);
@@ -69,11 +60,12 @@ public interface ExportedJobLauncher {
* @see JobLauncher#isRunning()
*/
boolean isRunning();
/**
* Query statistics of currently executing jobs.
*
* @return properties representing last known state of currently executing jobs
* @return properties representing last known state of currently executing
* jobs
*/
public Properties getStatistics();

View File

@@ -0,0 +1,65 @@
/*
* Copyright 2006-2007 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.execution.bootstrap.support;
import java.beans.PropertyEditorSupport;
import org.springframework.batch.core.domain.Job;
import org.springframework.batch.core.domain.JobIdentifier;
import org.springframework.batch.core.runtime.JobIdentifierFactory;
import org.springframework.batch.core.runtime.SimpleJobIdentifierFactory;
/**
* Simple adapter for a {@link JobIdentifierFactory} that can be used to convert
* from a {@link Job} name to a {@link JobIdentifier}.
*
* @author Dave Syer
*
*/
public class JobIdentifierPropertyEditor extends PropertyEditorSupport {
private JobIdentifierFactory jobIdentifierFactory = new SimpleJobIdentifierFactory();
/**
* Public setter for the {@link JobIdentifierFactory}.
* @param jobIdentifierFactory the jobIdentifierFactory to set
*/
public void setJobIdentifierFactory(JobIdentifierFactory jobIdentifierFactory) {
this.jobIdentifierFactory = jobIdentifierFactory;
}
/**
* Accept name of {@link Job} and create a {@link JobIdentifier}.
*
* @see java.beans.PropertyEditorSupport#setAsText(java.lang.String)
*/
public void setAsText(String text) throws IllegalArgumentException {
setValue(jobIdentifierFactory.getJobIdentifier(text));
}
/**
* Extract the name from the {@link JobIdentifier}.
*
* @see java.beans.PropertyEditorSupport#getAsText()
*/
public String getAsText() {
JobIdentifier identifier = (JobIdentifier) getValue();
if (identifier == null) {
return null;
}
return identifier.getName();
}
}

View File

@@ -24,9 +24,12 @@ import java.util.List;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.batch.support.DefaultPropertyEditorRegistrar;
import org.springframework.beans.PropertyEditorRegistry;
import org.springframework.beans.SimpleTypeConverter;
import org.springframework.beans.TypeConverter;
import org.springframework.beans.TypeMismatchException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
@@ -37,13 +40,32 @@ import org.springframework.util.ReflectionUtils;
* @author Dave Syer
*
*/
public class TypeConverterMethodInterceptor implements MethodInterceptor {
public class TypeConverterMethodInterceptor extends DefaultPropertyEditorRegistrar implements MethodInterceptor,
InitializingBean {
// Get the default PropertyEditorRegistry free.
private TypeConverter typeConverter = new SimpleTypeConverter();
private TypeConverter typeConverter;
private boolean convertException = false;
/**
* Ensure that the {@link TypeConverter} is set up, creating one if
* necessary, and if it is a {@link PropertyEditorRegistry}, register the
* custom editors. (If it is not a {@link PropertyEditorRegistry} then the
* custom editors are ignored).
*
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
*/
public void afterPropertiesSet() throws Exception {
if (typeConverter == null) {
SimpleTypeConverter converter = new SimpleTypeConverter();
typeConverter = converter;
}
if (typeConverter instanceof PropertyEditorRegistry) {
registerCustomEditors((PropertyEditorRegistry) typeConverter);
}
}
/**
* Set a flag that will cause exceptions during method invocation to be
* caught and treated as a result. This can be useful if used over JConsole,
@@ -107,7 +129,7 @@ public class TypeConverterMethodInterceptor implements MethodInterceptor {
}
});
if (methods.size()==1) {
if (methods.size() == 1) {
method = (Method) methods.get(0);
for (int i = 0; i < arguments.length; i++) {
Object arg = arguments[i];

View File

@@ -18,7 +18,6 @@ package org.springframework.batch.execution.repository.dao;
import java.util.List;
import org.springframework.batch.core.domain.Job;
import org.springframework.batch.core.domain.JobExecution;
import org.springframework.batch.core.domain.JobIdentifier;
import org.springframework.batch.core.domain.JobInstance;

View File

@@ -0,0 +1,62 @@
/*
* Copyright 2006-2007 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.execution.bootstrap.support;
import org.springframework.batch.core.domain.JobIdentifier;
import org.springframework.batch.core.runtime.SimpleJobIdentifierFactory;
import junit.framework.TestCase;
/**
* @author Dave Syer
*
*/
public class JobIdentifierPropertyEditorTests extends TestCase {
private JobIdentifierPropertyEditor editor = new JobIdentifierPropertyEditor();
/**
* Test method for {@link org.springframework.batch.execution.bootstrap.support.JobIdentifierPropertyEditor#setJobIdentifierFactory(org.springframework.batch.core.runtime.JobIdentifierFactory)}.
*/
public void testSetJobIdentifierFactory() {
editor.setJobIdentifierFactory(new SimpleJobIdentifierFactory() {
public JobIdentifier getJobIdentifier(String name) {
return super.getJobIdentifier("test:"+name);
}
});
editor.setAsText("foo");
JobIdentifier identifier = (JobIdentifier) editor.getValue();
assertEquals("test:foo", identifier.getName());
}
/**
* Test method for {@link org.springframework.batch.execution.bootstrap.support.JobIdentifierPropertyEditor#setAsText(java.lang.String)}.
*/
public void testSetAsTextString() {
editor.setAsText("foo");
JobIdentifier identifier = (JobIdentifier) editor.getValue();
assertEquals("foo", identifier.getName());
}
/**
* Test method for {@link org.springframework.batch.execution.bootstrap.support.JobIdentifierPropertyEditor#getAsText()}.
*/
public void testGetAsText() {
editor.setAsText("foo");
assertEquals("foo", editor.getAsText());
}
}

View File

@@ -17,6 +17,15 @@ public class TypeConverterMethodInterceptorTests extends TestCase {
private List list = new ArrayList();
/*
* (non-Javadoc)
* @see junit.framework.TestCase#setUp()
*/
protected void setUp() throws Exception {
super.setUp();
interceptor.afterPropertiesSet();
}
/**
* Even though TestBean does not implement Test, the proxy will invoke a
* method is called with the same signature.
@@ -72,6 +81,36 @@ public class TypeConverterMethodInterceptorTests extends TestCase {
assertEquals("FOO:true", proxy.getBean());
}
public void testInvokeWithNoMethodMatch() throws Exception {
ProxyFactory factory = new ProxyFactory(Test.class, interceptor);
factory.setTarget(this);
Test proxy = (Test) factory.getProxy();
assertNull(proxy.getBean());
}
public void testInvokeWithError() throws Exception {
ProxyFactory factory = new ProxyFactory(Test.class, interceptor);
factory.setTarget(new TestBean(true));
Test proxy = (Test) factory.getProxy();
try {
assertNull(proxy.error());
fail("Expected RuntimeException");
}
catch (RuntimeException e) {
// expected
assertEquals("Foo", e.getMessage());
}
}
public void testInvokeWithErrorAndConvert() throws Exception {
ProxyFactory factory = new ProxyFactory(Test.class, interceptor);
factory.setTarget(new TestBean(true));
Test proxy = (Test) factory.getProxy();
interceptor.setConvertException(true);
String msg = proxy.error();
assertTrue("Message is not a stacktrace: "+msg, msg.indexOf("RuntimeException: Foo")>=0);
}
public void testInvokeWithMethodParameterConversionToBoolean() throws Exception {
ProxyFactory factory = new ProxyFactory(Test.class, interceptor);
factory.setTarget(new TestBean(true));
@@ -94,11 +133,12 @@ public class TypeConverterMethodInterceptorTests extends TestCase {
try {
proxy.getInvalid();
fail("Expected TypeMismatchException");
} catch (TypeMismatchException e) {
}
catch (TypeMismatchException e) {
// expected
}
}
public void testTypeConverter() throws Exception {
final TestCase testCase = this;
interceptor.setTypeConverter(new SimpleTypeConverter() {
@@ -112,6 +152,12 @@ public class TypeConverterMethodInterceptorTests extends TestCase {
assertEquals(testCase, proxy.getInvalid());
}
public void testTypeConverterAfterPropertiesSet() throws Exception {
testTypeConverter();
interceptor.afterPropertiesSet();
testTypeConverter();
}
public void testInvokeWithMethodParameterConversionWithPropertyEditor() throws Exception {
final TestBean bean = new TestBean(false);
SimpleTypeConverter converter = new SimpleTypeConverter();
@@ -135,17 +181,19 @@ public class TypeConverterMethodInterceptorTests extends TestCase {
String relayPattern(String pattern);
TestCase getInvalid();
int getValue();
void operate();
String grab(String value);
String relayBean(String value);
String error();
}
// N.B. TestBean intentionally does not implement Test!
// N.B. TestBean intentionally does not implement Test!
public class TestBean {
private boolean test;
@@ -173,7 +221,7 @@ public class TypeConverterMethodInterceptorTests extends TestCase {
public TestBean getInvalid() {
return this;
}
public String getValue() {
return "123";
}
@@ -181,14 +229,18 @@ public class TypeConverterMethodInterceptorTests extends TestCase {
public void operate() {
list.add("FOO");
}
public String grab(boolean flag) {
return "flag:"+flag;
return "flag:" + flag;
}
public String toString() {
return "FOO:" + test;
}
public String error() {
throw new RuntimeException("Foo");
}
}
}

View File

@@ -16,7 +16,6 @@
package org.springframework.batch.io.file.mapping;
import java.beans.PropertyEditor;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
@@ -26,6 +25,7 @@ import java.util.Map;
import java.util.Properties;
import java.util.Set;
import org.springframework.batch.support.DefaultPropertyEditorRegistrar;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.NotWritablePropertyException;
@@ -35,9 +35,7 @@ import org.springframework.beans.PropertyEditorRegistry;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.CustomEditorConfigurer;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.validation.DataBinder;
import org.springframework.validation.ObjectError;
@@ -78,7 +76,7 @@ import org.springframework.validation.ObjectError;
* @author Dave Syer
*
*/
public class BeanWrapperFieldSetMapper implements FieldSetMapper, BeanFactoryAware, InitializingBean {
public class BeanWrapperFieldSetMapper extends DefaultPropertyEditorRegistrar implements FieldSetMapper, BeanFactoryAware, InitializingBean {
private String name;
@@ -86,8 +84,6 @@ public class BeanWrapperFieldSetMapper implements FieldSetMapper, BeanFactoryAwa
private BeanFactory beanFactory;
private Map customEditors;
private static Map propertiesMatched = new HashMap();
private static int distanceLimit = 5;
@@ -187,27 +183,10 @@ public class BeanWrapperFieldSetMapper implements FieldSetMapper, BeanFactoryAwa
DataBinder binder = new DataBinder(target);
binder.setIgnoreUnknownFields(false);
initBinder(binder);
registerPropertyEditors(binder);
registerCustomEditors(binder);
return binder;
}
/**
* Register property editors with the DataBinder. If any property editors
* have been supplied they are registered for use when binding a field set.
*
* @param binder new binder instance
*/
protected void registerPropertyEditors(DataBinder binder) {
if (this.customEditors != null) {
for (Iterator it = customEditors.entrySet().iterator(); it.hasNext();) {
Map.Entry entry = (Map.Entry) it.next();
Class key = (Class) entry.getKey();
PropertyEditor value = (PropertyEditor) entry.getValue();
binder.registerCustomEditor(key, value);
}
}
}
/**
* Initialize a new binder instance. This hook allows customization of
* binder settings such as the
@@ -336,38 +315,4 @@ public class BeanWrapperFieldSetMapper implements FieldSetMapper, BeanFactoryAwa
properties.setProperty(newName, value);
}
/**
* Specify the {@link PropertyEditor custom editors} to apply to target
* beans mapped with this {@link FieldSetMapper}.
*
*
* @param customEditors a map of Class to PropertyEditor (or class name to
* PropertyEditor).
* @see CustomEditorConfigurer#setCustomEditors(Map)
*/
public void setCustomEditors(Map customEditors) {
this.customEditors = new HashMap();
for (Iterator it = customEditors.entrySet().iterator(); it.hasNext();) {
Map.Entry entry = (Map.Entry) it.next();
Object key = entry.getKey();
Class requiredType = null;
if (key instanceof Class) {
requiredType = (Class) key;
}
else if (key instanceof String) {
String className = (String) key;
requiredType = ClassUtils.resolveClassName(className, getClass().getClassLoader());
}
else {
throw new IllegalArgumentException("Invalid key [" + key
+ "] for custom editor: needs to be Class or String.");
}
Object value = entry.getValue();
if (!(value instanceof PropertyEditor)) {
throw new IllegalArgumentException("Mapped value [" + value + "] for custom editor key [" + key
+ "] is not of required type [" + PropertyEditor.class.getName() + "]");
}
this.customEditors.put(requiredType, value);
}
}
}

View File

@@ -0,0 +1,90 @@
/*
* Copyright 2006-2007 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.support;
import java.beans.PropertyEditor;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.springframework.beans.PropertyEditorRegistrar;
import org.springframework.beans.PropertyEditorRegistry;
import org.springframework.beans.factory.config.CustomEditorConfigurer;
import org.springframework.util.ClassUtils;
/**
* A re-usable {@link PropertyEditorRegistrar} that can be used wherever one
* needs to register custom {@link PropertyEditor} instances with a
* {@link PropertyEditorRegistry} (like a bean wrapper, or a type converter).
*
* @author Dave Syer
*
*/
public class DefaultPropertyEditorRegistrar implements PropertyEditorRegistrar {
private Map customEditors;
/**
* Register the custom editors with the given registry.
*
* @see org.springframework.beans.PropertyEditorRegistrar#registerCustomEditors(org.springframework.beans.PropertyEditorRegistry)
*/
public void registerCustomEditors(PropertyEditorRegistry registry) {
if (this.customEditors != null) {
for (Iterator it = customEditors.entrySet().iterator(); it.hasNext();) {
Map.Entry entry = (Map.Entry) it.next();
Class key = (Class) entry.getKey();
PropertyEditor value = (PropertyEditor) entry.getValue();
registry.registerCustomEditor(key, value);
}
}
}
/**
* Specify the {@link PropertyEditor custom editors} to register.
*
*
* @param customEditors a map of Class to PropertyEditor (or class name to
* PropertyEditor).
* @see CustomEditorConfigurer#setCustomEditors(Map)
*/
public void setCustomEditors(Map customEditors) {
this.customEditors = new HashMap();
for (Iterator it = customEditors.entrySet().iterator(); it.hasNext();) {
Map.Entry entry = (Map.Entry) it.next();
Object key = entry.getKey();
Class requiredType = null;
if (key instanceof Class) {
requiredType = (Class) key;
}
else if (key instanceof String) {
String className = (String) key;
requiredType = ClassUtils.resolveClassName(className, getClass().getClassLoader());
}
else {
throw new IllegalArgumentException("Invalid key [" + key
+ "] for custom editor: needs to be Class or String.");
}
Object value = entry.getValue();
if (!(value instanceof PropertyEditor)) {
throw new IllegalArgumentException("Mapped value [" + value + "] for custom editor key [" + key
+ "] is not of required type [" + PropertyEditor.class.getName() + "]");
}
this.customEditors.put(requiredType, value);
}
}
}

View File

@@ -24,9 +24,6 @@ import java.util.Properties;
import junit.framework.TestCase;
import org.springframework.batch.io.file.mapping.BeanWrapperFieldSetMapper;
import org.springframework.batch.io.file.mapping.BindingException;
import org.springframework.batch.support.IntArrayPropertyEditor;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.NotWritablePropertyException;
import org.springframework.beans.propertyeditors.CustomNumberEditor;
@@ -252,17 +249,6 @@ public class BeanWrapperFieldSetMapperTests extends TestCase {
assertEquals('C', result.getVarChar());
}
public void testIntArray() throws Exception {
BeanWithIntArray result = new BeanWithIntArray();
BeanWrapperImpl wrapper = new BeanWrapperImpl(result);
wrapper.registerCustomEditor(int[].class, new IntArrayPropertyEditor());
PropertiesEditor editor = new PropertiesEditor();
editor.setAsText("numbers=1,2,3, 4");
Properties props = (Properties) editor.getValue();
wrapper.setPropertyValues(props);
assertEquals(4, result.numbers[3]);
}
// BeanWrapperFieldSetMapper doesn't currently support nesting with
// collections.
public void testNestedList() {
@@ -307,37 +293,6 @@ public class BeanWrapperFieldSetMapperTests extends TestCase {
}
}
public void testSetCustomEditorsWithInvalidTypeName() throws Exception {
BeanWrapperFieldSetMapper mapper = new BeanWrapperFieldSetMapper();
try {
mapper.setCustomEditors(Collections.singletonMap("FOO", new CustomNumberEditor(Long.class, true)));
} catch (IllegalArgumentException e) {
// expected
}
}
public void testSetCustomEditorsWithInvalidType() throws Exception {
BeanWrapperFieldSetMapper mapper = new BeanWrapperFieldSetMapper();
try {
mapper.setCustomEditors(Collections.singletonMap(new Object(), new CustomNumberEditor(Long.class, true)));
} catch (IllegalArgumentException e) {
// expected
}
}
public void testSetCustomEditorsWithInvalidEditor() throws Exception {
BeanWrapperFieldSetMapper mapper = new BeanWrapperFieldSetMapper();
try {
mapper.setCustomEditors(Collections.singletonMap(Long.class, "FOO"));
} catch (IllegalArgumentException e) {
// expected
}
}
public void testPaddedLongWithEditor() throws Exception {
BeanWrapperFieldSetMapper mapper = new BeanWrapperFieldSetMapper();
@@ -367,14 +322,6 @@ public class BeanWrapperFieldSetMapperTests extends TestCase {
assertEquals(78, bean.getVarInt());
}
private static class BeanWithIntArray {
private int[] numbers;
public void setNumbers(int[] numbers) {
this.numbers = numbers;
}
}
private static class TestNestedList {
List nestedC;

View File

@@ -0,0 +1,84 @@
/*
* Copyright 2006-2007 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.io.support;
import java.util.Collections;
import java.util.Properties;
import junit.framework.TestCase;
import org.springframework.batch.support.DefaultPropertyEditorRegistrar;
import org.springframework.batch.support.IntArrayPropertyEditor;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.propertyeditors.CustomNumberEditor;
import org.springframework.beans.propertyeditors.PropertiesEditor;
public class DefaultPropertEditorRegistrarTests extends TestCase {
public void testIntArray() throws Exception {
DefaultPropertyEditorRegistrar mapper = new DefaultPropertyEditorRegistrar();
BeanWithIntArray result = new BeanWithIntArray();
BeanWrapperImpl wrapper = new BeanWrapperImpl(result);
mapper.setCustomEditors(Collections.singletonMap(int[].class, new IntArrayPropertyEditor()));
mapper.registerCustomEditors(wrapper);
PropertiesEditor editor = new PropertiesEditor();
editor.setAsText("numbers=1,2,3, 4");
Properties props = (Properties) editor.getValue();
wrapper.setPropertyValues(props);
assertEquals(4, result.numbers[3]);
}
public void testSetCustomEditorsWithInvalidTypeName() throws Exception {
DefaultPropertyEditorRegistrar mapper = new DefaultPropertyEditorRegistrar();
try {
mapper.setCustomEditors(Collections.singletonMap("FOO", new CustomNumberEditor(Long.class, true)));
} catch (IllegalArgumentException e) {
// expected
}
}
public void testSetCustomEditorsWithInvalidType() throws Exception {
DefaultPropertyEditorRegistrar mapper = new DefaultPropertyEditorRegistrar();
try {
mapper.setCustomEditors(Collections.singletonMap(new Object(), new CustomNumberEditor(Long.class, true)));
} catch (IllegalArgumentException e) {
// expected
}
}
public void testSetCustomEditorsWithInvalidEditor() throws Exception {
DefaultPropertyEditorRegistrar mapper = new DefaultPropertyEditorRegistrar();
try {
mapper.setCustomEditors(Collections.singletonMap(Long.class, "FOO"));
} catch (IllegalArgumentException e) {
// expected
}
}
private static class BeanWithIntArray {
private int[] numbers;
public void setNumbers(int[] numbers) {
this.numbers = numbers;
}
}
}

View File

@@ -102,7 +102,14 @@
<bean id="convertingMethodInterceptor"
class="org.springframework.batch.execution.bootstrap.support.TypeConverterMethodInterceptor">
<property name="convertException" value="true" />
<property name="convertException" value="true" />
<property name="customEditors">
<map>
<entry key="org.springframework.batch.core.domain.JobIdentifier">
<bean class="org.springframework.batch.execution.bootstrap.support.JobIdentifierPropertyEditor"/>
</entry>
</map>
</property>
</bean>
<bean id="logAdvice"