From 1077405691f991f8c7cec7d605a324867023db7a Mon Sep 17 00:00:00 2001 From: dsyer Date: Wed, 13 Aug 2008 12:01:43 +0000 Subject: [PATCH] OPEN - issue BATCH-773: Refactor and extend ExportedJobLauncher to JobOperator Add additional methods and tests to SimpleJobOperator --- .../core/explore/BatchMetaDataExplorer.java | 19 +++ .../launch/support/SimpleJobOperator.java | 82 ++++++++---- .../repository/dao/JdbcJobExecutionDao.java | 4 +- .../support/SimpleJobOperatorTests.java | 117 ++++++++++++++++-- .../dao/AbstractJobExecutionDaoTests.java | 2 + .../batch/support/PropertiesConverter.java | 58 ++++++--- .../support/PropertiesConverterTests.java | 28 +++-- 7 files changed, 247 insertions(+), 63 deletions(-) diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/explore/BatchMetaDataExplorer.java b/spring-batch-core/src/main/java/org/springframework/batch/core/explore/BatchMetaDataExplorer.java index c4beaaa6e..353cfffbf 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/explore/BatchMetaDataExplorer.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/explore/BatchMetaDataExplorer.java @@ -16,6 +16,7 @@ package org.springframework.batch.core.explore; import java.util.List; +import java.util.Set; import org.springframework.batch.core.JobExecution; import org.springframework.batch.core.JobInstance; @@ -47,4 +48,22 @@ public interface BatchMetaDataExplorer { */ JobExecution getJobExecution(Long executionId); + /** + * @param instanceId + * @return the {@link JobInstance} with this id, or null + */ + JobInstance getJobInstance(Long instanceId); + + /** + * @param jobInstance the {@link JobInstance} to query + * @return the set of all executions for the specified {@link JobInstance} + */ + List findJobExecutions(JobInstance jobInstance); + + /** + * @param jobName the name of the job + * @return the set of running executions for jobs with the specified name + */ + Set findRunningJobExecutions(String jobName); + } diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/launch/support/SimpleJobOperator.java b/spring-batch-core/src/main/java/org/springframework/batch/core/launch/support/SimpleJobOperator.java index d31206023..edaf392f5 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/launch/support/SimpleJobOperator.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/launch/support/SimpleJobOperator.java @@ -15,6 +15,10 @@ */ package org.springframework.batch.core.launch.support; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -26,8 +30,9 @@ import org.springframework.batch.core.JobExecution; import org.springframework.batch.core.JobInstance; import org.springframework.batch.core.JobParameters; import org.springframework.batch.core.JobParametersIncrementer; +import org.springframework.batch.core.StepExecution; import org.springframework.batch.core.UnexpectedJobExecutionException; -import org.springframework.batch.core.configuration.JobLocator; +import org.springframework.batch.core.configuration.ListableJobRegistry; import org.springframework.batch.core.converter.DefaultJobParametersConverter; import org.springframework.batch.core.converter.JobParametersConverter; import org.springframework.batch.core.explore.BatchMetaDataExplorer; @@ -56,7 +61,7 @@ public class SimpleJobOperator implements JobOperator, InitializingBean { private static final String ILLEGAL_STATE_MSG = "Illegal state (only happens on a race condition): " + "%s with name=%s and parameters=%s"; - private JobLocator jobRegistry; + private ListableJobRegistry jobRegistry; private BatchMetaDataExplorer batchMetaDataExplorer; @@ -86,10 +91,10 @@ public class SimpleJobOperator implements JobOperator, InitializingBean { } /** - * Public setter for the {@link JobLocator}. - * @param jobRegistry the {@link JobLocator} to set + * Public setter for the {@link ListableJobRegistry}. + * @param jobRegistry the {@link ListableJobRegistry} to set */ - public void setJobLocator(JobLocator jobRegistry) { + public void setJobRegistry(ListableJobRegistry jobRegistry) { this.jobRegistry = jobRegistry; } @@ -117,8 +122,15 @@ public class SimpleJobOperator implements JobOperator, InitializingBean { * lang.Long) */ public List getExecutions(Long instanceId) throws NoSuchJobException { - // TODO Auto-generated method stub - return null; + JobInstance jobInstance = batchMetaDataExplorer.getJobInstance(instanceId); + if (jobInstance == null) { + throw new NoSuchJobException(String.format("No job instance with id=%d", instanceId)); + } + List list = new ArrayList(); + for (JobExecution jobExecution : batchMetaDataExplorer.findJobExecutions(jobInstance)) { + list.add(jobExecution.getId()); + } + return list; } /* @@ -127,8 +139,7 @@ public class SimpleJobOperator implements JobOperator, InitializingBean { * @see org.springframework.batch.core.launch.JobOperator#getJobNames() */ public Set getJobNames() { - // TODO Auto-generated method stub - return null; + return new HashSet(jobRegistry.getJobNames()); } /* @@ -139,8 +150,11 @@ public class SimpleJobOperator implements JobOperator, InitializingBean { * .lang.String, int) */ public List getLastInstances(String jobName, int count) throws NoSuchJobException { - // TODO Auto-generated method stub - return null; + List list = new ArrayList(); + for (JobInstance jobInstance : batchMetaDataExplorer.getLastJobInstances(jobName, count)) { + list.add(jobInstance.getId()); + } + return list; } /* @@ -151,8 +165,12 @@ public class SimpleJobOperator implements JobOperator, InitializingBean { * lang.Long) */ public String getParameters(Long executionId) throws NoSuchJobExecutionException { - // TODO Auto-generated method stub - return null; + JobExecution jobExecution = batchMetaDataExplorer.getJobExecution(executionId); + if (jobExecution == null) { + throw new NoSuchJobExecutionException(String.format("No job execution with id=%d", executionId)); + } + return PropertiesConverter.propertiesToString(jobParametersConverter.getProperties(jobExecution + .getJobInstance().getJobParameters())); } /* @@ -163,8 +181,11 @@ public class SimpleJobOperator implements JobOperator, InitializingBean { * (java.lang.String) */ public Set getRunningExecutions(String jobName) throws NoSuchJobException { - // TODO Auto-generated method stub - return null; + Set set = new LinkedHashSet(); + for (JobExecution jobExecution : batchMetaDataExplorer.findRunningJobExecutions(jobName)) { + set.add(jobExecution.getId()); + } + return set; } /* @@ -175,8 +196,15 @@ public class SimpleJobOperator implements JobOperator, InitializingBean { * (java.lang.Long) */ public Map getStepExecutionSummaries(Long executionId) throws NoSuchJobExecutionException { - // TODO Auto-generated method stub - return null; + JobExecution jobExecution = batchMetaDataExplorer.getJobExecution(executionId); + if (jobExecution == null) { + throw new NoSuchJobExecutionException(String.format("No job execution with id=%d", executionId)); + } + Map map = new LinkedHashMap(); + for (StepExecution stepExecution : jobExecution.getStepExecutions()) { + map.put(stepExecution.getId(), stepExecution.toString()); + } + return map; } /* @@ -188,7 +216,7 @@ public class SimpleJobOperator implements JobOperator, InitializingBean { */ public String getSummary(Long executionId) throws NoSuchJobExecutionException { JobExecution jobExecution = batchMetaDataExplorer.getJobExecution(executionId); - if (jobExecution==null) { + if (jobExecution == null) { throw new NoSuchJobExecutionException(String.format("No job execution with id=%d", executionId)); } return jobExecution.toString(); @@ -211,7 +239,7 @@ public class SimpleJobOperator implements JobOperator, InitializingBean { } String jobName = jobExecution.getJobInstance().getJobName(); - Job job = jobRegistry.getJob(jobName ); + Job job = jobRegistry.getJob(jobName); JobParameters parameters = jobExecution.getJobInstance().getJobParameters(); logger.info(String.format("Attempting to resume job with name=%s and parameters=%s", jobName, parameters)); @@ -219,8 +247,8 @@ public class SimpleJobOperator implements JobOperator, InitializingBean { return jobLauncher.run(job, parameters).getId(); } catch (JobExecutionAlreadyRunningException e) { - throw new UnexpectedJobExecutionException(String.format(ILLEGAL_STATE_MSG, - "job execution already running", jobName, parameters), e); + throw new UnexpectedJobExecutionException(String.format(ILLEGAL_STATE_MSG, "job execution already running", + jobName, parameters), e); } } @@ -252,16 +280,16 @@ public class SimpleJobOperator implements JobOperator, InitializingBean { return jobLauncher.run(job, jobParameters).getId(); } catch (JobExecutionAlreadyRunningException e) { - throw new UnexpectedJobExecutionException(String.format(ILLEGAL_STATE_MSG, - "job execution already running", jobName, parameters), e); + throw new UnexpectedJobExecutionException(String.format(ILLEGAL_STATE_MSG, "job execution already running", + jobName, parameters), e); } catch (JobRestartException e) { - throw new UnexpectedJobExecutionException(String.format(ILLEGAL_STATE_MSG, "job not restartable", - jobName, parameters), e); + throw new UnexpectedJobExecutionException(String.format(ILLEGAL_STATE_MSG, "job not restartable", jobName, + parameters), e); } catch (JobInstanceAlreadyCompleteException e) { - throw new UnexpectedJobExecutionException(String.format(ILLEGAL_STATE_MSG, "job already complete", - jobName, parameters), e); + throw new UnexpectedJobExecutionException(String.format(ILLEGAL_STATE_MSG, "job already complete", jobName, + parameters), e); } } diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/repository/dao/JdbcJobExecutionDao.java b/spring-batch-core/src/main/java/org/springframework/batch/core/repository/dao/JdbcJobExecutionDao.java index 8b33f39cf..35c4309d4 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/repository/dao/JdbcJobExecutionDao.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/repository/dao/JdbcJobExecutionDao.java @@ -229,8 +229,10 @@ public class JdbcJobExecutionDao extends AbstractJdbcBatchMetadataDao implements public JobExecution getJobExecution(Long executionId) { Long instanceId = getJdbcTemplate().queryForLong(getQuery(GET_INSTANCE_BY_EXECUTION_ID), executionId); JobInstance jobInstance = jobInstanceDao.getJobInstance(instanceId); - return getJdbcTemplate().queryForObject(getQuery(GET_EXECUTION_BY_ID), new JobExecutionRowMapper(jobInstance), + JobExecution jobExecution = getJdbcTemplate().queryForObject(getQuery(GET_EXECUTION_BY_ID), new JobExecutionRowMapper(jobInstance), executionId); + stepExecutionDao.getStepExecutions(jobExecution); + return jobExecution; } /* diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/launch/support/SimpleJobOperatorTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/launch/support/SimpleJobOperatorTests.java index d1184bee1..196111ab9 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/launch/support/SimpleJobOperatorTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/launch/support/SimpleJobOperatorTests.java @@ -19,8 +19,13 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import java.util.Arrays; +import java.util.Collection; import java.util.Collections; +import java.util.List; +import java.util.Map; import java.util.Properties; +import java.util.Set; import org.easymock.EasyMock; import org.junit.Before; @@ -30,7 +35,7 @@ import org.springframework.batch.core.JobExecution; import org.springframework.batch.core.JobInstance; import org.springframework.batch.core.JobParameters; import org.springframework.batch.core.JobParametersIncrementer; -import org.springframework.batch.core.configuration.JobLocator; +import org.springframework.batch.core.configuration.support.MapJobRegistry; import org.springframework.batch.core.converter.DefaultJobParametersConverter; import org.springframework.batch.core.explore.BatchMetaDataExplorer; import org.springframework.batch.core.job.JobSupport; @@ -40,6 +45,8 @@ import org.springframework.batch.core.launch.NoSuchJobException; import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException; import org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException; import org.springframework.batch.core.repository.JobRestartException; +import org.springframework.batch.core.step.StepSupport; +import org.springframework.batch.support.PropertiesConverter; /** * @author Dave Syer @@ -72,35 +79,44 @@ public class SimpleJobOperatorTests { }; } }; - + jobOperator = new SimpleJobOperator(); - - jobOperator.setJobLocator(new JobLocator() { + + jobOperator.setJobRegistry(new MapJobRegistry() { public Job getJob(String name) throws NoSuchJobException { if (name.equals("foo")) { return job; } throw new NoSuchJobException("foo"); } + @Override + public Collection getJobNames() { + return Arrays.asList(new String[] {"foo", "bar"}); + } }); - + jobOperator.setJobLauncher(new JobLauncher() { public JobExecution run(Job job, JobParameters jobParameters) throws JobExecutionAlreadyRunningException, JobRestartException, JobInstanceAlreadyCompleteException { return new JobExecution(new JobInstance(123L, jobParameters, job.getName()), 999L); } }); - + batchMetaDataExplorer = EasyMock.createNiceMock(BatchMetaDataExplorer.class); - + jobOperator.setBatchMetaDataExplorer(batchMetaDataExplorer); jobOperator.setJobParametersConverter(new DefaultJobParametersConverter() { @Override public JobParameters getJobParameters(Properties props) { - assertTrue("Wrong properties",props.containsKey("a")); + assertTrue("Wrong properties", props.containsKey("a")); return jobParameters; } + + @Override + public Properties getProperties(JobParameters params) { + return PropertiesConverter.stringToProperties("a=b"); + } }); jobOperator.afterPropertiesSet(); @@ -119,6 +135,17 @@ public class SimpleJobOperatorTests { } } + @Test + public void testStop() throws Exception { + try { + jobOperator.stop(123L); + fail("Expected UnsupportedOperationException"); + } + catch (UnsupportedOperationException e) { + // expected + } + } + /** * Test method for * {@link org.springframework.batch.core.launch.support.SimpleJobOperator#startNextInstance(java.lang.String)} @@ -167,7 +194,8 @@ public class SimpleJobOperatorTests { public void testResumeSunnyDay() throws Exception { jobParameters = new JobParameters(); batchMetaDataExplorer.getJobExecution(111L); - EasyMock.expectLastCall().andReturn(new JobExecution(new JobInstance(123L, jobParameters, job.getName()), 111L)); + EasyMock.expectLastCall() + .andReturn(new JobExecution(new JobInstance(123L, jobParameters, job.getName()), 111L)); EasyMock.replay(batchMetaDataExplorer); Long value = jobOperator.resume(111L); assertEquals(999, value.longValue()); @@ -186,5 +214,76 @@ public class SimpleJobOperatorTests { EasyMock.verify(batchMetaDataExplorer); } + @Test + public void testGetStepExecutionSummariesSunnyDay() throws Exception { + jobParameters = new JobParameters(); + batchMetaDataExplorer.getJobExecution(111L); + JobExecution jobExecution = new JobExecution(new JobInstance(123L, jobParameters, job.getName()), 111L); + jobExecution.createStepExecution(new StepSupport("step1")); + jobExecution.createStepExecution(new StepSupport("step2")); + jobExecution.getStepExecutions().iterator().next().setId(21L); + EasyMock.expectLastCall().andReturn(jobExecution); + EasyMock.replay(batchMetaDataExplorer); + Map value = jobOperator.getStepExecutionSummaries(111L); + assertEquals(2, value.size()); + EasyMock.verify(batchMetaDataExplorer); + } + + @Test + public void testFindRunningExecutionsSunnyDay() throws Exception { + jobParameters = new JobParameters(); + batchMetaDataExplorer.findRunningJobExecutions("foo"); + JobExecution jobExecution = new JobExecution(new JobInstance(123L, jobParameters, job.getName()), 111L); + EasyMock.expectLastCall().andReturn(Collections.singleton(jobExecution)); + EasyMock.replay(batchMetaDataExplorer); + Set value = jobOperator.getRunningExecutions("foo"); + assertEquals(111L, value.iterator().next().longValue()); + EasyMock.verify(batchMetaDataExplorer); + } + + @Test + public void testGetJobParametersSunnyDay() throws Exception { + final JobParameters jobParameters = new JobParameters(); + batchMetaDataExplorer.getJobExecution(111L); + EasyMock.expectLastCall() + .andReturn(new JobExecution(new JobInstance(123L, jobParameters, job.getName()), 111L)); + EasyMock.replay(batchMetaDataExplorer); + String value = jobOperator.getParameters(111L); + assertEquals("a=b", value); + EasyMock.verify(batchMetaDataExplorer); + } + + @Test + public void testGetLastInstancesSunnyDay() throws Exception { + jobParameters = new JobParameters(); + batchMetaDataExplorer.getLastJobInstances("foo",2); + JobInstance jobInstance = new JobInstance(123L, jobParameters, job.getName()); + EasyMock.expectLastCall().andReturn(Collections.singletonList(jobInstance)); + EasyMock.replay(batchMetaDataExplorer); + List value = jobOperator.getLastInstances("foo",2); + assertEquals(123L, value.get(0).longValue()); + EasyMock.verify(batchMetaDataExplorer); + } + + @Test + public void testGetJobNames() throws Exception { + Set names = jobOperator.getJobNames(); + assertEquals(2, names.size()); + assertTrue("Wrong names: "+names, names.contains("foo")); + } + + @Test + public void testGetExecutionsSunnyDay() throws Exception { + JobInstance jobInstance = new JobInstance(123L, jobParameters, job.getName()); + batchMetaDataExplorer.getJobInstance(123L); + EasyMock.expectLastCall().andReturn(jobInstance); + JobExecution jobExecution = new JobExecution(jobInstance, 111L); + batchMetaDataExplorer.findJobExecutions(jobInstance); + EasyMock.expectLastCall().andReturn(Collections.singletonList(jobExecution)); + EasyMock.replay(batchMetaDataExplorer); + List value = jobOperator.getExecutions(123L); + assertEquals(111L, value.iterator().next().longValue()); + EasyMock.verify(batchMetaDataExplorer); + } } diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/repository/dao/AbstractJobExecutionDaoTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/repository/dao/AbstractJobExecutionDaoTests.java index 0907eb3a4..8a0efbc94 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/repository/dao/AbstractJobExecutionDaoTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/repository/dao/AbstractJobExecutionDaoTests.java @@ -142,11 +142,13 @@ public abstract class AbstractJobExecutionDaoTests extends AbstractTransactional public void testGetExecution() { JobExecution exec = new JobExecution(jobInstance); exec.setCreateTime(new Date(0)); + exec.createStepExecution(new StepSupport("foo")); dao.saveJobExecution(exec); JobExecution value = dao.getJobExecution(exec.getId()); assertEquals(exec, value); + assertEquals(1, value.getStepExecutions().size()); } } diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/support/PropertiesConverter.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/support/PropertiesConverter.java index a45643b6b..98837c913 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/support/PropertiesConverter.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/support/PropertiesConverter.java @@ -19,6 +19,8 @@ package org.springframework.batch.support; import java.io.IOException; import java.io.StringReader; import java.io.StringWriter; +import java.util.Arrays; +import java.util.List; import java.util.Properties; import org.springframework.util.DefaultPropertiesPersister; @@ -26,11 +28,13 @@ import org.springframework.util.PropertiesPersister; import org.springframework.util.StringUtils; /** - * Utility to convert a Properties object to a String and back. Ideally this utility should have been used to convert to - * string in order to convert that string back to a Properties Object. Attempting to convert a string obtained by - * calling Properties.toString() will return an invalid Properties object. The format of Properties is that used by - * {@link PropertiesPersister} from the Spring Core, so a String in the correct format for a Spring property editor is - * fine (key=value pairs separated by new lines). + * Utility to convert a Properties object to a String and back. Ideally this + * utility should have been used to convert to string in order to convert that + * string back to a Properties Object. Attempting to convert a string obtained + * by calling Properties.toString() will return an invalid Properties object. + * The format of Properties is that used by {@link PropertiesPersister} from the + * Spring Core, so a String in the correct format for a Spring property editor + * is fine (key=value pairs separated by new lines). * * @author Lucas Ward * @author Dave Syer @@ -41,14 +45,18 @@ public final class PropertiesConverter { private static final PropertiesPersister propertiesPersister = new DefaultPropertiesPersister(); + private static final String LINE_SEPARATOR = System.getProperty("line.separator"); + // prevents the class from being instantiated private PropertiesConverter() { }; /** - * Parse a String to a Properties object. If string is null, an empty Properties object will be returned. The input - * String is a set of name=value pairs, delimited by either newline or comma (for brevity). If the input String - * contains a newline it is assumed that the separator is newline, otherwise comma. + * Parse a String to a Properties object. If string is null, an empty + * Properties object will be returned. The input String is a set of + * name=value pairs, delimited by either newline or comma (for brevity). If + * the input String contains a newline it is assumed that the separator is + * newline, otherwise comma. * * @param stringToParse String to parse. * @return Properties parsed from each string. @@ -62,7 +70,7 @@ public final class PropertiesConverter { if (!contains(stringToParse, "\n")) { return StringUtils.splitArrayElementsIntoProperties(StringUtils - .commaDelimitedListToStringArray(stringToParse), "="); + .commaDelimitedListToStringArray(stringToParse), "="); } StringReader stringReader = new StringReader(stringToParse); @@ -73,18 +81,20 @@ public final class PropertiesConverter { propertiesPersister.load(properties, stringReader); // Exception is only thrown by StringReader after it is closed, // so never in this case. - } catch (IOException ex) { + } + catch (IOException ex) { throw new IllegalStateException("Error while trying to parse String to java.util.Properties," - + " given String: " + properties); + + " given String: " + properties); } return properties; } /** - * Convert Properties object to String. This is only necessary for compatibility with converting the String back to - * a properties object. If an empty properties object is passed in, a blank string is returned, otherwise it's - * string representation is returned. + * Convert Properties object to String. This is only necessary for + * compatibility with converting the String back to a properties object. If + * an empty properties object is passed in, a blank string is returned, + * otherwise it's string representation is returned. * * @param propertiesToParse * @return String representation of properties object @@ -100,12 +110,28 @@ public final class PropertiesConverter { try { propertiesPersister.store(propertiesToParse, stringWriter, null); - } catch (IOException ex) { + } + catch (IOException ex) { // Exception is never thrown by StringWriter throw new IllegalStateException("Error while trying to convert properties to string"); } - return stringWriter.toString(); + // If the value is short enough (and doesn't contain commas), convert to + // comma-separated... + String value = stringWriter.toString(); + if (value.length() < 160) { + List list = Arrays.asList(StringUtils.delimitedListToStringArray(value, LINE_SEPARATOR, + LINE_SEPARATOR)); + String shortValue = StringUtils.collectionToCommaDelimitedString(list.subList(1, list.size())); + int count = StringUtils.countOccurrencesOf(shortValue, ","); + if (count == list.size() - 2) { + value = shortValue; + } + if (value.endsWith(",")) { + value = value.substring(0, value.length() - 1); + } + } + return value; } private static boolean contains(String str, String searchStr) { diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/support/PropertiesConverterTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/support/PropertiesConverterTests.java index f962841c3..4001d2407 100644 --- a/spring-batch-infrastructure/src/test/java/org/springframework/batch/support/PropertiesConverterTests.java +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/support/PropertiesConverterTests.java @@ -18,6 +18,8 @@ package org.springframework.batch.support; import java.util.Properties; +import org.springframework.util.StringUtils; + import junit.framework.TestCase; /** @@ -73,6 +75,22 @@ public class PropertiesConverterTests extends TestCase { assertEquals(storedProps, props); } + /** + * Check that Properties can be comma delimited with extra whitespace. + */ + public void testShortConversionWithCommas() { + + Properties storedProps = new Properties(); + storedProps.setProperty("key1", "value1"); + storedProps.setProperty("key2", "value2"); + + String value = PropertiesConverter.propertiesToString(storedProps); + + assertTrue("Wrong value: "+value, value.contains("key1=value1")); + assertTrue("Wrong value: "+value, value.contains("key2=value2")); + assertEquals(1, StringUtils.countOccurrencesOf(value, ",")); + } + /** * Check that Properties can be newline delimited. */ @@ -87,16 +105,6 @@ public class PropertiesConverterTests extends TestCase { assertEquals(storedProps, props); } - /** - * Converting a String to Properties and back does not return equal String! - * See {@link PropertiesConverter} javadoc for more details. - */ - public void testInvalidConversion() { - String value = "key=value"; - string = PropertiesConverter.propertiesToString(PropertiesConverter.stringToProperties(value)); - assertFalse(value.equals(string)); - } - /** * Null String should be converted to empty Properties */