diff --git a/spring-batch-execution/src/main/java/org/springframework/batch/execution/bootstrap/support/DefaultJobParametersFactory.java b/spring-batch-execution/src/main/java/org/springframework/batch/execution/bootstrap/support/DefaultJobParametersFactory.java index c866803ef..8d313217f 100644 --- a/spring-batch-execution/src/main/java/org/springframework/batch/execution/bootstrap/support/DefaultJobParametersFactory.java +++ b/spring-batch-execution/src/main/java/org/springframework/batch/execution/bootstrap/support/DefaultJobParametersFactory.java @@ -71,7 +71,7 @@ public class DefaultJobParametersFactory implements JobParametersFactory { */ public JobParameters getJobParameters(Properties props) { - if(props == null || props.isEmpty()){ + if (props == null || props.isEmpty()) { return new JobParameters(); } @@ -87,7 +87,9 @@ public class DefaultJobParametersFactory implements JobParametersFactory { date = dateFormat.parse(value); } catch (ParseException ex) { - throw new IllegalArgumentException("Date format is invalid: [" + value + "], use " + dateFormat, ex); + String suffix = (dateFormat instanceof SimpleDateFormat) ? ", use " + + ((SimpleDateFormat) dateFormat).toPattern() : ""; + throw new IllegalArgumentException("Date format is invalid: [" + value + "]" + suffix, ex); } propertiesBuilder.addDate(StringUtils.replace(key, DATE_TYPE, ""), date); } @@ -97,8 +99,10 @@ public class DefaultJobParametersFactory implements JobParametersFactory { result = (Long) numberFormat.parse(value); } catch (ParseException ex) { + String suffix = (numberFormat instanceof DecimalFormat) ? ", use " + + ((DecimalFormat) numberFormat).toPattern() : ""; throw new IllegalArgumentException( - "Number format is invalid: [" + value + "], use " + numberFormat, ex); + "Number format is invalid: [" + value + "], use " + suffix, ex); } catch (ClassCastException ex) { throw new IllegalArgumentException("Number format is invalid: [" + value @@ -124,22 +128,24 @@ public class DefaultJobParametersFactory implements JobParametersFactory { * @see org.springframework.batch.core.runtime.JobParametersFactory#getProperties(org.springframework.batch.core.domain.JobParameters) */ public Properties getProperties(JobParameters params) { - - if(params == null || params.isEmpty()){ + + if (params == null || params.isEmpty()) { return new Properties(); } - + Map parameters = params.getParameters(); Properties result = new Properties(); for (Iterator iterator = parameters.keySet().iterator(); iterator.hasNext();) { String key = (String) iterator.next(); Object value = parameters.get(key); if (value instanceof Date) { - result.setProperty(key+DATE_TYPE, dateFormat.format(value)); - } else if (value instanceof Long) { - result.setProperty(key+LONG_TYPE, numberFormat.format(value)); - } else { - result.setProperty(key,""+value); + result.setProperty(key + DATE_TYPE, dateFormat.format(value)); + } + else if (value instanceof Long) { + result.setProperty(key + LONG_TYPE, numberFormat.format(value)); + } + else { + result.setProperty(key, "" + value); } } return result; @@ -152,4 +158,14 @@ public class DefaultJobParametersFactory implements JobParametersFactory { public void setDateFormat(DateFormat dateFormat) { this.dateFormat = dateFormat; } + + /** + * Public setter for the {@link NumberFormat}. Used to parse longs, so must + * not contain decimal place (e.g. use "#" or "#,###"). + * + * @param numberFormat the {@link NumberFormat} to set + */ + public void setNumberFormat(NumberFormat numberFormat) { + this.numberFormat = numberFormat; + } } diff --git a/spring-batch-execution/src/main/java/org/springframework/batch/execution/bootstrap/support/ScheduledJobParametersFactory.java b/spring-batch-execution/src/main/java/org/springframework/batch/execution/bootstrap/support/ScheduledJobParametersFactory.java index 4711ae0f5..356435ab4 100644 --- a/spring-batch-execution/src/main/java/org/springframework/batch/execution/bootstrap/support/ScheduledJobParametersFactory.java +++ b/spring-batch-execution/src/main/java/org/springframework/batch/execution/bootstrap/support/ScheduledJobParametersFactory.java @@ -58,7 +58,7 @@ public class ScheduledJobParametersFactory implements JobParametersFactory { scheduleDate = dateFormat.parse(entry.getValue().toString()); } catch (ParseException ex) { - throw new IllegalArgumentException("Schedule date format is invalid: [" + entry.getValue() + "]", + throw new IllegalArgumentException("Date format is invalid: [" + entry.getValue() + "]", ex); } propertiesBuilder.addDate(entry.getKey().toString(), scheduleDate); diff --git a/spring-batch-execution/src/main/java/org/springframework/batch/execution/step/simple/AbstractStep.java b/spring-batch-execution/src/main/java/org/springframework/batch/execution/step/simple/AbstractStep.java index a2cdb9949..d89d85034 100644 --- a/spring-batch-execution/src/main/java/org/springframework/batch/execution/step/simple/AbstractStep.java +++ b/spring-batch-execution/src/main/java/org/springframework/batch/execution/step/simple/AbstractStep.java @@ -151,8 +151,9 @@ public abstract class AbstractStep extends StepSupport { if (streamManager == null) { manager = new SimpleStreamManager(transactionManager); } - SimpleStepExecutor executor = new SimpleStepExecutor(manager, this); + SimpleStepExecutor executor = new SimpleStepExecutor(this); executor.setRepository(jobRepository); + executor.setStreamManager(manager); executor.applyConfiguration(this); executor.setTasklet(tasklet); return executor; diff --git a/spring-batch-execution/src/main/java/org/springframework/batch/execution/step/simple/SimpleStepExecutor.java b/spring-batch-execution/src/main/java/org/springframework/batch/execution/step/simple/SimpleStepExecutor.java index b70ecb56e..d6cfff9da 100644 --- a/spring-batch-execution/src/main/java/org/springframework/batch/execution/step/simple/SimpleStepExecutor.java +++ b/spring-batch-execution/src/main/java/org/springframework/batch/execution/step/simple/SimpleStepExecutor.java @@ -90,9 +90,8 @@ public class SimpleStepExecutor { /** * Package private constructor so the step can create a the executor. */ - SimpleStepExecutor(StreamManager streamManager, AbstractStep abstractStep) { + SimpleStepExecutor(AbstractStep abstractStep) { this.step = abstractStep; - this.streamManager = streamManager; } /** @@ -393,16 +392,6 @@ public class SimpleStepExecutor { /** * Apply the configuration by inspecting it to see if it has any relevant * policy information. - * * * @param step a step */ diff --git a/spring-batch-execution/src/test/java/org/springframework/batch/execution/bootstrap/support/DefaultJobParametersFactoryTests.java b/spring-batch-execution/src/test/java/org/springframework/batch/execution/bootstrap/support/DefaultJobParametersFactoryTests.java index c082a3c85..5d76f5e46 100644 --- a/spring-batch-execution/src/test/java/org/springframework/batch/execution/bootstrap/support/DefaultJobParametersFactoryTests.java +++ b/spring-batch-execution/src/test/java/org/springframework/batch/execution/bootstrap/support/DefaultJobParametersFactoryTests.java @@ -16,6 +16,7 @@ package org.springframework.batch.execution.bootstrap.support; import java.text.DateFormat; +import java.text.DecimalFormat; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Properties; @@ -52,6 +53,67 @@ public class DefaultJobParametersFactoryTests extends TestCase { assertEquals(date, props.getDate("schedule.date")); } + public void testGetParametersWithDateFormat() throws Exception { + + String[] args = new String[] { "schedule.date(date)=2008/23/01" }; + + factory.setDateFormat(new SimpleDateFormat("yyyy/dd/MM")); + JobParameters props = factory.getJobParameters(StringUtils.splitArrayElementsIntoProperties(args, "=")); + assertNotNull(props); + Date date = dateFormat.parse("01/23/2008"); + assertEquals(date, props.getDate("schedule.date")); + } + + public void testGetParametersWithBogusDate() throws Exception { + + String[] args = new String[] { "schedule.date(date)=20080123" }; + + try { + factory.getJobParameters(StringUtils.splitArrayElementsIntoProperties(args, "=")); + } catch (IllegalArgumentException e) { + String message = e.getMessage(); + assertTrue("Message should contain wrong date: "+message, message.contains("20080123")); + assertTrue("Message should contain format: "+message, message.contains("yyyy/MM/dd")); + } + } + + public void testGetParametersWithNumberFormat() throws Exception { + + String[] args = new String[] { "value(long)=1,000" }; + + factory.setNumberFormat(new DecimalFormat("#,###")); + JobParameters props = factory.getJobParameters(StringUtils.splitArrayElementsIntoProperties(args, "=")); + assertNotNull(props); + assertEquals(1000L, props.getLong("value").longValue()); + } + + public void testGetParametersWithBogusLong() throws Exception { + + String[] args = new String[] { "value(long)=foo" }; + + try { + factory.getJobParameters(StringUtils.splitArrayElementsIntoProperties(args, "=")); + } catch (IllegalArgumentException e) { + String message = e.getMessage(); + assertTrue("Message should contain wrong number: "+message, message.contains("foo")); + assertTrue("Message should contain format: "+message, message.contains("#")); + } + } + + public void testGetParametersWithDouble() throws Exception { + + String[] args = new String[] { "value(long)=1.03" }; + factory.setNumberFormat(new DecimalFormat("#.#")); + + try { + factory.getJobParameters(StringUtils.splitArrayElementsIntoProperties(args, "=")); + } catch (IllegalArgumentException e) { + String message = e.getMessage(); + assertTrue("Message should contain wrong number: "+message, message.contains("1.03")); + assertTrue("Message should contain 'decimal': "+message, message.contains("decimal")); + } + } + public void testGetProperties() throws Exception { JobParameters parameters = new JobParametersBuilder().addDate("schedule.date", dateFormat.parse("01/23/2008")) diff --git a/spring-batch-execution/src/test/java/org/springframework/batch/execution/bootstrap/support/ScheduledJobParametersFactoryTests.java b/spring-batch-execution/src/test/java/org/springframework/batch/execution/bootstrap/support/ScheduledJobParametersFactoryTests.java index 50338ea23..ac6dc6fcf 100644 --- a/spring-batch-execution/src/test/java/org/springframework/batch/execution/bootstrap/support/ScheduledJobParametersFactoryTests.java +++ b/spring-batch-execution/src/test/java/org/springframework/batch/execution/bootstrap/support/ScheduledJobParametersFactoryTests.java @@ -80,4 +80,28 @@ public class ScheduledJobParametersFactoryTests extends TestCase { assertEquals(new JobParameters(), factory.getJobParameters(null)); assertEquals(new Properties(), factory.getProperties(null)); } + + public void testGetParametersWithDateFormat() throws Exception { + + String[] args = new String[] { "schedule.date=2008/23/01" }; + + factory.setDateFormat(new SimpleDateFormat("yyyy/dd/MM")); + JobParameters props = factory.getJobParameters(StringUtils.splitArrayElementsIntoProperties(args, "=")); + assertNotNull(props); + Date date = dateFormat.parse("01/23/2008"); + assertEquals(date, props.getDate("schedule.date")); + } + + public void testGetParametersWithBogusDate() throws Exception { + + String[] args = new String[] { "schedule.date=20080123" }; + + try { + factory.getJobParameters(StringUtils.splitArrayElementsIntoProperties(args, "=")); + } catch (IllegalArgumentException e) { + String message = e.getMessage(); + assertTrue("Message should contain wrong date: "+message, message.contains("20080123")); + } + } + } diff --git a/spring-batch-execution/src/test/java/org/springframework/batch/execution/bootstrap/support/SimpleExportedJobLauncherTests.java b/spring-batch-execution/src/test/java/org/springframework/batch/execution/bootstrap/support/SimpleExportedJobLauncherTests.java new file mode 100644 index 000000000..b613bbb80 --- /dev/null +++ b/spring-batch-execution/src/test/java/org/springframework/batch/execution/bootstrap/support/SimpleExportedJobLauncherTests.java @@ -0,0 +1,208 @@ +/* + * 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.util.ArrayList; +import java.util.List; +import java.util.Properties; + +import junit.framework.TestCase; + +import org.springframework.batch.core.domain.Job; +import org.springframework.batch.core.domain.JobExecution; +import org.springframework.batch.core.domain.JobParameters; +import org.springframework.batch.core.domain.JobParametersBuilder; +import org.springframework.batch.core.domain.JobSupport; +import org.springframework.batch.core.domain.StepExecution; +import org.springframework.batch.core.domain.StepInstance; +import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException; +import org.springframework.batch.core.runtime.JobParametersFactory; +import org.springframework.batch.execution.configuration.MapJobRegistry; +import org.springframework.batch.execution.launch.JobLauncher; +import org.springframework.batch.item.StreamContext; +import org.springframework.batch.support.PropertiesConverter; + +/** + * @author Dave Syer + * + */ +public class SimpleExportedJobLauncherTests extends TestCase { + + private SimpleExportedJobLauncher launcher = new SimpleExportedJobLauncher(); + + private MapJobRegistry jobLocator; + + private List list = new ArrayList(); + + protected void setUp() throws Exception { + super.setUp(); + launcher.setLauncher(new JobLauncher() { + public JobExecution run(Job job, JobParameters jobParameters) throws JobExecutionAlreadyRunningException { + JobExecution result = new JobExecution(null); + StepExecution stepExecution = result.createStepExecution(new StepInstance(null, "step")); + stepExecution.setStreamContext(new StreamContext(PropertiesConverter.stringToProperties("foo=bar"))); + list.add(jobParameters); + return result; + } + }); + jobLocator = new MapJobRegistry(); + launcher.setJobLocator(jobLocator); + } + + /** + * Test method for + * {@link org.springframework.batch.execution.bootstrap.support.SimpleExportedJobLauncher#afterPropertiesSet()}. + * @throws Exception + */ + public void testAfterPropertiesSet() throws Exception { + launcher = new SimpleExportedJobLauncher(); + try { + launcher.afterPropertiesSet(); + fail("Expected IllegalArgumentException"); + } + catch (IllegalArgumentException e) { + String message = e.getMessage(); + assertTrue("Message does not contain 'launcher': " + message, message.toLowerCase().contains("joblauncher")); + } + } + + /** + * Test method for + * {@link org.springframework.batch.execution.bootstrap.support.SimpleExportedJobLauncher#afterPropertiesSet()}. + * @throws Exception + */ + public void testAfterPropertiesSetWithLauncher() throws Exception { + launcher = new SimpleExportedJobLauncher(); + launcher.setLauncher(new JobLauncher() { + public JobExecution run(Job job, JobParameters jobParameters) throws JobExecutionAlreadyRunningException { + return null; + } + }); + try { + launcher.afterPropertiesSet(); + fail("Expected IllegalArgumentException"); + } + catch (IllegalArgumentException e) { + String message = e.getMessage(); + assertTrue("Message does not contain 'locator': " + message, message.toLowerCase().contains("joblocator")); + } + } + + /** + * Test method for + * {@link org.springframework.batch.execution.bootstrap.support.SimpleExportedJobLauncher#getStatistics()}. + */ + public void testGetStatistics() { + Properties props = launcher.getStatistics(); + assertNotNull(props); + assertEquals(0, props.entrySet().size()); + } + + /** + * Test method for + * {@link org.springframework.batch.execution.bootstrap.support.SimpleExportedJobLauncher#getStatistics()}. + * @throws Exception + */ + public void testGetStatisticsWithContent() throws Exception { + jobLocator.register(new JobSupport("foo")); + launcher.run("foo"); + Properties props = launcher.getStatistics(); + assertNotNull(props); + assertEquals(1, props.entrySet().size()); + } + + /** + * Test method for + * {@link org.springframework.batch.execution.bootstrap.support.SimpleExportedJobLauncher#isRunning()}. + * @throws Exception + */ + public void testIsRunning() throws Exception { + jobLocator.register(new JobSupport("foo")); + launcher.run("foo"); + assertTrue(launcher.isRunning()); + } + + /** + * Test method for + * {@link org.springframework.batch.execution.bootstrap.support.SimpleExportedJobLauncher#isRunning()}. + * @throws Exception + */ + public void testAlreadyRunning() throws Exception { + jobLocator.register(new JobSupport("foo")); + launcher.setLauncher(new JobLauncher() { + public JobExecution run(Job job, JobParameters jobParameters) throws JobExecutionAlreadyRunningException { + throw new JobExecutionAlreadyRunningException("Bad!"); + } + }); + String value = launcher.run("foo"); + assertTrue("Return value was not an exception: " + value, value.contains("JobExecutionAlreadyRunningException")); + } + + /** + * Test method for + * {@link org.springframework.batch.execution.bootstrap.support.SimpleExportedJobLauncher#run(java.lang.String)}. + */ + public void testRunNonExistentJob() { + String value = launcher.run("foo"); + assertTrue("Return value was not an exception: " + value, value.contains("NoSuchJobException")); + } + + /** + * Test method for + * {@link org.springframework.batch.execution.bootstrap.support.SimpleExportedJobLauncher#run(java.lang.String)}. + * @throws Exception + */ + public void testRunJobWithParameters() throws Exception { + jobLocator.register(new JobSupport("foo")); + String value = launcher.run("foo", "bar=spam,bucket=crap"); + assertTrue(launcher.isRunning()); + assertTrue("Return value was not a JobExecution: " + value, value.contains("JobExecution")); + } + + /** + * Test method for + * {@link org.springframework.batch.execution.bootstrap.support.SimpleExportedJobLauncher#run(java.lang.String)}. + * @throws Exception + */ + public void testRunJobWithParametersAndFactory() throws Exception { + jobLocator.register(new JobSupport("foo")); + launcher.setJobParametersFactory(new JobParametersFactory() { + public JobParameters getJobParameters(Properties properties) { + return new JobParametersBuilder().addString("foo", "spam").toJobParameters(); + } + public Properties getProperties(JobParameters params) { + return null; + } + }); + launcher.run("foo", "bar=spam,bucket=crap"); + assertEquals(1, list.size()); + assertEquals("spam", ((JobParameters) list.get(0)).getString("foo")); + } + + /** + * Test method for + * {@link org.springframework.batch.execution.bootstrap.support.SimpleExportedJobLauncher#stop()}. + * @throws Exception + */ + public void testStop() throws Exception { + jobLocator.register(new JobSupport("foo")); + launcher.run("foo"); + assertTrue(launcher.isRunning()); + launcher.stop(); + assertFalse(launcher.isRunning()); + } + +}