From badcf74c7bdc7e294bec38ccc995e8d197479064 Mon Sep 17 00:00:00 2001 From: dsyer Date: Mon, 21 Jan 2008 11:37:12 +0000 Subject: [PATCH] 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. --- .../support/ExportedJobLauncher.java | 32 +++---- .../support/JobIdentifierPropertyEditor.java | 65 ++++++++++++++ .../TypeConverterMethodInterceptor.java | 28 +++++- .../execution/repository/dao/JobDao.java | 1 - .../JobIdentifierPropertyEditorTests.java | 62 +++++++++++++ .../TypeConverterMethodInterceptorTests.java | 70 +++++++++++++-- .../mapping/BeanWrapperFieldSetMapper.java | 61 +------------ .../DefaultPropertyEditorRegistrar.java | 90 +++++++++++++++++++ .../BeanWrapperFieldSetMapperTests.java | 53 ----------- .../DefaultPropertEditorRegistrarTests.java | 84 +++++++++++++++++ .../src/main/resources/jobs/adhocLoopJob.xml | 9 +- 11 files changed, 410 insertions(+), 145 deletions(-) create mode 100644 spring-batch-execution/src/main/java/org/springframework/batch/execution/bootstrap/support/JobIdentifierPropertyEditor.java create mode 100644 spring-batch-execution/src/test/java/org/springframework/batch/execution/bootstrap/support/JobIdentifierPropertyEditorTests.java create mode 100644 spring-batch-infrastructure/src/main/java/org/springframework/batch/support/DefaultPropertyEditorRegistrar.java create mode 100644 spring-batch-infrastructure/src/test/java/org/springframework/batch/io/support/DefaultPropertEditorRegistrarTests.java diff --git a/spring-batch-execution/src/main/java/org/springframework/batch/execution/bootstrap/support/ExportedJobLauncher.java b/spring-batch-execution/src/main/java/org/springframework/batch/execution/bootstrap/support/ExportedJobLauncher.java index 1afbc2859..df6ecc3b2 100644 --- a/spring-batch-execution/src/main/java/org/springframework/batch/execution/bootstrap/support/ExportedJobLauncher.java +++ b/spring-batch-execution/src/main/java/org/springframework/batch/execution/bootstrap/support/ExportedJobLauncher.java @@ -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(); diff --git a/spring-batch-execution/src/main/java/org/springframework/batch/execution/bootstrap/support/JobIdentifierPropertyEditor.java b/spring-batch-execution/src/main/java/org/springframework/batch/execution/bootstrap/support/JobIdentifierPropertyEditor.java new file mode 100644 index 000000000..510f5f145 --- /dev/null +++ b/spring-batch-execution/src/main/java/org/springframework/batch/execution/bootstrap/support/JobIdentifierPropertyEditor.java @@ -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(); + } +} \ No newline at end of file diff --git a/spring-batch-execution/src/main/java/org/springframework/batch/execution/bootstrap/support/TypeConverterMethodInterceptor.java b/spring-batch-execution/src/main/java/org/springframework/batch/execution/bootstrap/support/TypeConverterMethodInterceptor.java index dc9723eef..1f9b9fd5a 100644 --- a/spring-batch-execution/src/main/java/org/springframework/batch/execution/bootstrap/support/TypeConverterMethodInterceptor.java +++ b/spring-batch-execution/src/main/java/org/springframework/batch/execution/bootstrap/support/TypeConverterMethodInterceptor.java @@ -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]; diff --git a/spring-batch-execution/src/main/java/org/springframework/batch/execution/repository/dao/JobDao.java b/spring-batch-execution/src/main/java/org/springframework/batch/execution/repository/dao/JobDao.java index 4597b5ce0..5781cfd41 100644 --- a/spring-batch-execution/src/main/java/org/springframework/batch/execution/repository/dao/JobDao.java +++ b/spring-batch-execution/src/main/java/org/springframework/batch/execution/repository/dao/JobDao.java @@ -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; diff --git a/spring-batch-execution/src/test/java/org/springframework/batch/execution/bootstrap/support/JobIdentifierPropertyEditorTests.java b/spring-batch-execution/src/test/java/org/springframework/batch/execution/bootstrap/support/JobIdentifierPropertyEditorTests.java new file mode 100644 index 000000000..71e118170 --- /dev/null +++ b/spring-batch-execution/src/test/java/org/springframework/batch/execution/bootstrap/support/JobIdentifierPropertyEditorTests.java @@ -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()); + } + +} diff --git a/spring-batch-execution/src/test/java/org/springframework/batch/execution/bootstrap/support/TypeConverterMethodInterceptorTests.java b/spring-batch-execution/src/test/java/org/springframework/batch/execution/bootstrap/support/TypeConverterMethodInterceptorTests.java index 7f0ac1d36..eab003316 100644 --- a/spring-batch-execution/src/test/java/org/springframework/batch/execution/bootstrap/support/TypeConverterMethodInterceptorTests.java +++ b/spring-batch-execution/src/test/java/org/springframework/batch/execution/bootstrap/support/TypeConverterMethodInterceptorTests.java @@ -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"); + } } } diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/io/file/mapping/BeanWrapperFieldSetMapper.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/io/file/mapping/BeanWrapperFieldSetMapper.java index 7791fb3ee..d67557fc7 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/io/file/mapping/BeanWrapperFieldSetMapper.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/io/file/mapping/BeanWrapperFieldSetMapper.java @@ -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); - } - } } diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/support/DefaultPropertyEditorRegistrar.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/support/DefaultPropertyEditorRegistrar.java new file mode 100644 index 000000000..d3b478183 --- /dev/null +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/support/DefaultPropertyEditorRegistrar.java @@ -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); + } + } + +} diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/io/file/mapping/BeanWrapperFieldSetMapperTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/io/file/mapping/BeanWrapperFieldSetMapperTests.java index e84d5971c..55fc1b246 100644 --- a/spring-batch-infrastructure/src/test/java/org/springframework/batch/io/file/mapping/BeanWrapperFieldSetMapperTests.java +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/io/file/mapping/BeanWrapperFieldSetMapperTests.java @@ -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; diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/io/support/DefaultPropertEditorRegistrarTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/io/support/DefaultPropertEditorRegistrarTests.java new file mode 100644 index 000000000..73c4e3d95 --- /dev/null +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/io/support/DefaultPropertEditorRegistrarTests.java @@ -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; + } + } + +} diff --git a/spring-batch-samples/src/main/resources/jobs/adhocLoopJob.xml b/spring-batch-samples/src/main/resources/jobs/adhocLoopJob.xml index 9522b1da3..7ab538998 100644 --- a/spring-batch-samples/src/main/resources/jobs/adhocLoopJob.xml +++ b/spring-batch-samples/src/main/resources/jobs/adhocLoopJob.xml @@ -102,7 +102,14 @@ - + + + + + + + +