From ea93f7a4c7397a25d9f3d036b8fbcef8a3447f08 Mon Sep 17 00:00:00 2001 From: lucasward Date: Mon, 7 Jan 2008 06:19:06 +0000 Subject: [PATCH] BATCH-84: Modified JobIdentifier interface to include new JobRuntimeParameters value object, and modified its subclasses accordingly. --- spring-batch-core/.project | 60 +++--- .../batch/core/domain/JobIdentifier.java | 16 +- .../core/domain/JobRuntimeParameters.java | 178 ++++++++++++++++++ .../domain/JobRuntimeParametersBuilder.java | 88 +++++++++ .../core/runtime/SimpleJobIdentifier.java | 44 +++-- .../JobRuntimeParametersBuilderTests.java | 36 ++++ .../domain/JobRuntimeParametersTests.java | 122 ++++++++++++ .../runtime/SimpleJobIdentifierTests.java | 9 - 8 files changed, 497 insertions(+), 56 deletions(-) create mode 100644 spring-batch-core/src/main/java/org/springframework/batch/core/domain/JobRuntimeParameters.java create mode 100644 spring-batch-core/src/main/java/org/springframework/batch/core/domain/JobRuntimeParametersBuilder.java create mode 100644 spring-batch-core/src/test/java/org/springframework/batch/core/domain/JobRuntimeParametersBuilderTests.java create mode 100644 spring-batch-core/src/test/java/org/springframework/batch/core/domain/JobRuntimeParametersTests.java diff --git a/spring-batch-core/.project b/spring-batch-core/.project index 0dfc18b4e..aee145583 100644 --- a/spring-batch-core/.project +++ b/spring-batch-core/.project @@ -1,30 +1,30 @@ - - - spring-batch-core - Core domain for batch processing, expressing a domain of Jobs, Steps, Chunks, etc. - - spring-batch-infrastructure - - - - org.eclipse.jdt.core.javabuilder - - - - - org.springframework.ide.eclipse.core.springbuilder - - - - - org.devzuz.q.maven.jdt.core.mavenIncrementalBuilder - - - - - - org.springframework.ide.eclipse.core.springnature - org.eclipse.jdt.core.javanature - org.devzuz.q.maven.jdt.core.mavenNature - - + + + spring-batch-core + Core domain for batch processing, expressing a domain of Jobs, Steps, Chunks, etc. + + spring-batch-infrastructure + + + + org.eclipse.jdt.core.javabuilder + + + + + org.springframework.ide.eclipse.core.springbuilder + + + + + org.devzuz.q.maven.jdt.core.mavenIncrementalBuilder + + + + + + org.springframework.ide.eclipse.core.springnature + org.eclipse.jdt.core.javanature + org.devzuz.q.maven.jdt.core.mavenNature + + diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/domain/JobIdentifier.java b/spring-batch-core/src/main/java/org/springframework/batch/core/domain/JobIdentifier.java index cc3e0e6d1..686cdab8b 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/domain/JobIdentifier.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/domain/JobIdentifier.java @@ -16,15 +16,15 @@ package org.springframework.batch.core.domain; + /** * Identifier strategy for {@link JobInstance}. Different batch projects can * have different approaches and requirements regarding the identity of a job. - * The minimum requirement is to provide a unique name to identify a job. Other - * applications or projects might add other properties like a schedule date, or - * an additional label (code). + * The minimum requirement is to provide a unique name to identify a job and JobRuntimeParameters. * * @author Dave Syer - * + * @author Lucas Ward + * @since 1.0 */ public interface JobIdentifier { @@ -34,5 +34,13 @@ public interface JobIdentifier { * @return the name of the job */ public String getName(); + + /** + * A simple getter for the {@link JobRuntimeParameters} that also identify + * this job. + * + * @return JobRuntimeParameters + */ + public JobRuntimeParameters getRuntimeParameters(); } diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/domain/JobRuntimeParameters.java b/spring-batch-core/src/main/java/org/springframework/batch/core/domain/JobRuntimeParameters.java new file mode 100644 index 000000000..2f2d52d91 --- /dev/null +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/domain/JobRuntimeParameters.java @@ -0,0 +1,178 @@ +/** + * + */ +package org.springframework.batch.core.domain; + +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; + +import org.apache.commons.lang.builder.HashCodeBuilder; +import org.springframework.util.Assert; + +/** + * Value object representing runtime parameters to a batch job. Because + * the parameters have no individual meaning outside of the JobIdentifier they + * are contained within, it is a value object rather than an entity. It is also + * extremely important that a parameters object can be reliably compared to another + * for equality, in order to determine if one JobIdentifier equals another. + * Furthermore, because these parameters will need to be persisted, it is vital + * that the types added are restricted. + * + * @author Lucas Ward + * @since 1.0 + */ +public class JobRuntimeParameters { + + private final Map stringMap; + + private final Map longMap; + + private final Map dateMap; + + /** + * Default constructor. Creates a new empty JobRuntimeParameters. It should be noted + * that this constructor should only be used if an empty parameters is needed, since + * JobRuntimeParameters is immutable. + */ + public JobRuntimeParameters(){ + this.stringMap = new HashMap(); + this.longMap = new HashMap(); + this.dateMap = new HashMap(); + } + + /** + * Create a new parameters object based upon three maps for each of the three + * data types. See {@link JobRuntimeParametersBuilder} for an easier way to + * create paramters. + * + * @param stringMap + * @param longMap + * @param dateMap + */ + public JobRuntimeParameters(Map stringMap, Map longMap, Map dateMap){ + super(); + + validateMap(stringMap, String.class); + validateMap(longMap, Long.class); + validateMap(dateMap, Date.class); + this.stringMap = stringMap; + this.longMap = longMap; + this.dateMap = copyDateMap(dateMap); + } + + /** + * Typesafe Getter for the String represented by the provided key. + * + * @param key + * @return + */ + public String getString(String key){ + return (String)stringMap.get(key); + } + + /** + * Typesafe Getter for the Long represented by the provided key. + * + * @param key + * @return + */ + public Long getLong(String key){ + return (Long)longMap.get(key); + } + + /** + * Typesafe Getter for the Date represented by the provided key. + * + * @param key + * @return + */ + public Date getDate(String key){ + return (Date)dateMap.get(key); + } + + /** + * Get a map of all parameters, including string, long, and date. It should be noted + * that a Collections$UnmodifiableMap is returned, ensuring immutability. + * + * @return an unmodifiable map containing all parameters. + */ + public Map getParameters(){ + Map tempMap = new HashMap(stringMap); + tempMap.putAll(longMap); + tempMap.putAll(dateMap); + return Collections.unmodifiableMap(tempMap); + } + + /* + * Convenience method for validating that a the provided map only contains a particular + * type as a value, with only a String as a key. + */ + private void validateMap(Map map, Class type){ + + for(Iterator it = map.entrySet().iterator();it.hasNext();){ + + Entry entry = (Entry)it.next(); + if(entry.getKey() instanceof String == false){ + throw new IllegalArgumentException("All parameter keys must be strings."); + } + if(entry.getValue().getClass() != type){ + throw new IllegalArgumentException("The values in this map must be of type:[" + type + + "]."); + } + } + } + + /* + * Convenience method for copying Date values to ensure immutability. + */ + private Map copyDateMap(Map dateMap){ + Map tempMap = new HashMap(); + + for(Iterator it = dateMap.entrySet().iterator();it.hasNext();){ + Entry entry = (Entry)it.next(); + Date date = (Date)entry.getValue(); + tempMap.put(entry.getKey(), new Date(date.getTime())); + } + + return tempMap; + } + + public boolean equals(Object obj) { + + if(obj instanceof JobRuntimeParameters == false){ + return false; + } + + if(this == obj){ + return true; + } + + JobRuntimeParameters parameters = (JobRuntimeParameters)obj; + + //Since the type contained by each map is known, it's safe to call Map.equals() + if(getParameters().equals(parameters.getParameters())){ + return true; + } + else{ + return false; + } + } + + public int hashCode() { + + return new HashCodeBuilder(7, 21). + append(stringMap). + append(longMap). + append(dateMap). + toHashCode(); + } + + public String toString() { + + return stringMap.toString() + longMap.toString() + dateMap.toString(); + } +} diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/domain/JobRuntimeParametersBuilder.java b/spring-batch-core/src/main/java/org/springframework/batch/core/domain/JobRuntimeParametersBuilder.java new file mode 100644 index 000000000..aafbee3cf --- /dev/null +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/domain/JobRuntimeParametersBuilder.java @@ -0,0 +1,88 @@ +/** + * + */ +package org.springframework.batch.core.domain; + +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +import org.springframework.util.Assert; + +/** + * Helper class for creating JobRuntimeParameters. Useful because of all JobRuntimeParameters + * are immutable, and require 3 separate maps of the three supported types to ensure typesafety. + * Once created, it can be used in the same was a java.lang.StringBuilder (except, order is irrelevant), + * by adding various parameters types and creating a valid JobRuntimeParametres once finished. + * + * @author Lucas Ward + * @since 1.0 + * @see JobRuntimeParameters + */ +public class JobRuntimeParametersBuilder { + + private final Map stringMap; + + private final Map longMap; + + private final Map dateMap; + + /** + * Default constructor. Initializes the builder + */ + public JobRuntimeParametersBuilder() { + + this.stringMap = new HashMap(); + this.longMap = new HashMap(); + this.dateMap = new HashMap(); + } + + /** + * Add a new String parameter for the given key. + * + * @param key - parameter accessor. + * @param parameter - runtime parameter + * @return a refernece to this object. + */ + public JobRuntimeParametersBuilder addString(String key, String parameter){ + Assert.notNull(parameter, "Parameter must not be null."); + stringMap.put(key, parameter); + return this; + } + + /** + * Add a new Date parameter for the given key. + * + * @param key - parameter accessor. + * @param parameter - runtime parameter + * @return a refernece to this object. + */ + public JobRuntimeParametersBuilder addDate(String key, Date parameter){ + Assert.notNull(parameter, "Parameter must not be null."); + dateMap.put(key, new Date(parameter.getTime())); + return this; + } + + /** + * Add a new Long parameter for the given key. + * + * @param key - parameter accessor. + * @param parameter - runtime parameter + * @return a refernece to this object. + */ + public JobRuntimeParametersBuilder addLong(String key, Long parameter){ + Assert.notNull(parameter, "Parameter must not be null."); + longMap.put(key, parameter); + return this; + } + + /** + * Conversion method that takes the current state of this builder and returns it as + * a JobruntimeParameters object. + * + * @return a valid JobRuntimeParameters object. + */ + public JobRuntimeParameters toJobRuntimeParameters(){ + return new JobRuntimeParameters(stringMap, longMap, dateMap); + } +} diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/runtime/SimpleJobIdentifier.java b/spring-batch-core/src/main/java/org/springframework/batch/core/runtime/SimpleJobIdentifier.java index de00adaf9..0df4232b0 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/runtime/SimpleJobIdentifier.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/runtime/SimpleJobIdentifier.java @@ -18,22 +18,27 @@ package org.springframework.batch.core.runtime; import org.apache.commons.lang.builder.EqualsBuilder; import org.apache.commons.lang.builder.HashCodeBuilder; import org.springframework.batch.core.domain.JobIdentifier; +import org.springframework.batch.core.domain.JobRuntimeParameters; import org.springframework.util.ClassUtils; /** + * Simple, immutable, implementation of the JobIdentifier interface. + * * @author Dave Syer + * @author Lucas Ward * */ public class SimpleJobIdentifier implements JobIdentifier { private String name; - + private JobRuntimeParameters runtimeParameters; /** - * Default constructor. + * Default constructor. Since there it is required that the Identifier at least have a name, + * the default constructor should not be called. */ - public SimpleJobIdentifier() { + private SimpleJobIdentifier() { super(); } @@ -42,8 +47,12 @@ public class SimpleJobIdentifier implements JobIdentifier { * @param name */ public SimpleJobIdentifier(String name) { - super(); + this(name, new JobRuntimeParameters()); + } + + public SimpleJobIdentifier(String name, JobRuntimeParameters runtimeParameters){ this.name = name; + this.runtimeParameters = runtimeParameters; } /* (non-Javadoc) @@ -53,24 +62,33 @@ public class SimpleJobIdentifier implements JobIdentifier { return this.name; } - /** - * Public setter for the name. - * - * @param name the name to set - */ - public void setName(String name) { - this.name = name; + public JobRuntimeParameters getRuntimeParameters() { + return runtimeParameters; } public String toString() { - return ClassUtils.getShortName(getClass())+": name=" + name; + return ClassUtils.getShortName(getClass())+": name=" + name + "parameters=" + runtimeParameters; } public boolean equals(Object obj) { - return EqualsBuilder.reflectionEquals(this, obj); + + if(obj instanceof SimpleJobIdentifier == false){ + return false; + } + + if(this == obj){ + return true; + } + + SimpleJobIdentifier rhs = (SimpleJobIdentifier)obj; + return new EqualsBuilder(). + append(runtimeParameters,rhs.getRuntimeParameters()). + append(name, rhs.getName()). + isEquals(); } public int hashCode() { return HashCodeBuilder.reflectionHashCode(this); } + } diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/domain/JobRuntimeParametersBuilderTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/domain/JobRuntimeParametersBuilderTests.java new file mode 100644 index 000000000..79557f4be --- /dev/null +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/domain/JobRuntimeParametersBuilderTests.java @@ -0,0 +1,36 @@ +/** + * + */ +package org.springframework.batch.core.domain; + +import java.util.Date; + +import junit.framework.TestCase; + +/** + * @author Lucas Ward + * + */ +public class JobRuntimeParametersBuilderTests extends TestCase { + + JobRuntimeParametersBuilder parametersBuilder; + + Date date = new Date(System.currentTimeMillis()); + + protected void setUp() throws Exception { + super.setUp(); + + parametersBuilder = new JobRuntimeParametersBuilder(); + parametersBuilder.addDate("SCHEDULE_DATE", date); + parametersBuilder.addLong("LONG", new Long(1)); + parametersBuilder.addString("STRING", "string value"); + } + + public void testToJobRuntimeParamters(){ + + JobRuntimeParameters parameters = parametersBuilder.toJobRuntimeParameters(); + assertEquals(parameters.getDate("SCHEDULE_DATE"), date); + assertEquals(parameters.getLong("LONG"), new Long(1)); + assertEquals(parameters.getString("STRING"), "string value"); + } +} diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/domain/JobRuntimeParametersTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/domain/JobRuntimeParametersTests.java new file mode 100644 index 000000000..825d2ba00 --- /dev/null +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/domain/JobRuntimeParametersTests.java @@ -0,0 +1,122 @@ +/** + * + */ +package org.springframework.batch.core.domain; + +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +import junit.framework.TestCase; + +/** + * @author Lucas Ward + * + */ +public class JobRuntimeParametersTests extends TestCase { + + JobRuntimeParameters parameters; + + Map stringMap; + + Map longMap; + + Map dateMap; + Date date1 = new Date(4321431242L); + Date date2 = new Date(7809089900L); + + protected void setUp() throws Exception { + super.setUp(); + + parameters = getNewParameters(); + } + + private JobRuntimeParameters getNewParameters(){ + + stringMap = new HashMap(); + stringMap.put("string.key1", "value1"); + stringMap.put("string.key2", "value2"); + + longMap = new HashMap(); + longMap.put("long.key1", new Long(1)); + longMap.put("long.key2", new Long(2)); + + dateMap = new HashMap(); + dateMap.put("date.key1", date1 ); + dateMap.put("date.key2", date2 ); + + return new JobRuntimeParameters(stringMap, longMap, dateMap); + } + + public void testBadLongConstructorException() throws Exception{ + + Map badLongMap = new HashMap(); + badLongMap.put("key", "bad long"); + + try{ + JobRuntimeParameters testParameters = new JobRuntimeParameters(stringMap, badLongMap, dateMap); + fail(); + } + catch(IllegalArgumentException ex){ + //expected + } + } + + public void testBadStringConstructorException() throws Exception{ + + Map badMap = new HashMap(); + badMap.put("key", new Integer(2)); + + try{ + JobRuntimeParameters testParameters = new JobRuntimeParameters(badMap, longMap, dateMap); + fail(); + } + catch(IllegalArgumentException ex){ + //expected + } + } + + public void testBadDateConstructorException() throws Exception{ + + Map badMap = new HashMap(); + badMap.put("key", new java.sql.Date(System.currentTimeMillis())); + + try{ + JobRuntimeParameters testParameters = new JobRuntimeParameters(stringMap, longMap, badMap); + fail(); + } + catch(IllegalArgumentException ex){ + //expected + } + } + + public void testGetString(){ + + assertEquals("value1", parameters.getString("string.key1")); + assertEquals("value2", parameters.getString("string.key2")); + } + + public void testGetLong(){ + + assertEquals(new Long(1), parameters.getLong("long.key1")); + assertEquals(new Long(2), parameters.getLong("long.key2")); + } + + public void testGetDate(){ + + assertEquals(date1, parameters.getDate("date.key1")); + assertEquals(date2, parameters.getDate("date.key2")); + } + + public void testEquals(){ + + JobRuntimeParameters testParameters = getNewParameters(); + assertTrue(testParameters.equals(parameters)); + } + + public void testToString(){ + + assertEquals("{string.key1=value1, string.key2=value2}{long.key1=1, long.key2=2}" + + "{date.key2=Wed Apr 01 03:11:29 CST 1970, date.key1=Thu Feb 19 18:23:51 CST 1970}", parameters.toString()); + } +} diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/runtime/SimpleJobIdentifierTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/runtime/SimpleJobIdentifierTests.java index 1223fa8fc..30bf648a1 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/runtime/SimpleJobIdentifierTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/runtime/SimpleJobIdentifierTests.java @@ -25,13 +25,6 @@ public class SimpleJobIdentifierTests extends TestCase { private SimpleJobIdentifier identifier = new SimpleJobIdentifier("foo"); - /** - * Test method for - * {@link org.springframework.batch.core.runtime.SimpleJobIdentifier#SimpleJobIdentifier()}. - */ - public void testSimpleJobIdentifier() { - assertNull(new SimpleJobIdentifier().getName()); - } /** * Test method for @@ -39,8 +32,6 @@ public class SimpleJobIdentifierTests extends TestCase { */ public void testGetName() { assertEquals("foo", identifier.getName()); - identifier.setName("bar"); - assertEquals("bar", identifier.getName()); } /**