diff --git a/execution/src/main/java/org/springframework/batch/execution/facade/BatchResourceFactoryBean.java b/execution/src/main/java/org/springframework/batch/execution/facade/BatchResourceFactoryBean.java index 30a509165..25ca46698 100644 --- a/execution/src/main/java/org/springframework/batch/execution/facade/BatchResourceFactoryBean.java +++ b/execution/src/main/java/org/springframework/batch/execution/facade/BatchResourceFactoryBean.java @@ -17,7 +17,13 @@ package org.springframework.batch.execution.facade; import java.io.File; +import java.text.SimpleDateFormat; +import org.springframework.batch.core.domain.JobIdentifier; +import org.springframework.batch.core.domain.StepExecution; +import org.springframework.batch.execution.runtime.ScheduledJobIdentifier; +import org.springframework.batch.execution.scope.StepContext; +import org.springframework.batch.execution.scope.StepContextAware; import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.config.AbstractFactoryBean; import org.springframework.context.ResourceLoaderAware; @@ -51,7 +57,7 @@ import org.springframework.util.StringUtils; * * @see FactoryBean */ -public class BatchResourceFactoryBean extends AbstractFactoryBean implements ResourceLoaderAware { +public class BatchResourceFactoryBean extends AbstractFactoryBean implements ResourceLoaderAware, StepContextAware { private static final String BATCH_ROOT_PATTERN = "%BATCH_ROOT%"; @@ -75,6 +81,8 @@ public class BatchResourceFactoryBean extends AbstractFactoryBean implements Res private String jobStream = ""; private int jobRun = 0; + + private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd"); private String scheduleDate = ""; @@ -83,6 +91,15 @@ public class BatchResourceFactoryBean extends AbstractFactoryBean implements Res private String stepName = ""; private ResourceLoader resourceLoader; + + /** + * Always false because we are usually expecting to be step scoped. + * + * @see org.springframework.beans.factory.config.AbstractFactoryBean#isSingleton() + */ + public boolean isSingleton() { + return false; + } /* * (non-Javadoc) @@ -92,6 +109,25 @@ public class BatchResourceFactoryBean extends AbstractFactoryBean implements Res this.resourceLoader = resourceLoader; } + /* + * (non-Javadoc) + * @see org.springframework.batch.execution.scope.StepContextAware#setStepScopeContext(org.springframework.core.AttributeAccessor) + */ + public void setStepContext(StepContext context) { + Assert.state(context.getStepExecution()!=null, "The StepContext does not have an execution."); + StepExecution execution = context.getStepExecution(); + stepName = execution.getStep().getName(); + jobName = execution.getStep().getJob().getName(); + JobIdentifier identifier = execution.getJobExecution().getJobIdentifier(); + if (identifier instanceof ScheduledJobIdentifier) { + ScheduledJobIdentifier scheduledJobIdentifier = (ScheduledJobIdentifier) identifier; + jobStream = scheduledJobIdentifier.getJobStream(); + jobRun = scheduledJobIdentifier.getJobRun(); + scheduleDate = dateFormat.format(scheduledJobIdentifier.getScheduleDate()); + } + + } + /** * Returns the Resource representing the file defined by the file pattern. * @@ -177,5 +213,9 @@ public class BatchResourceFactoryBean extends AbstractFactoryBean implements Res public void setScheduleDate(String scheduleDate) { this.scheduleDate = scheduleDate; } + + public void setDateFormatPattern(String pattern) { + dateFormat = new SimpleDateFormat(pattern); + } } diff --git a/execution/src/main/java/org/springframework/batch/execution/scope/StepContextAware.java b/execution/src/main/java/org/springframework/batch/execution/scope/StepContextAware.java index 98c273e18..c36397f4d 100644 --- a/execution/src/main/java/org/springframework/batch/execution/scope/StepContextAware.java +++ b/execution/src/main/java/org/springframework/batch/execution/scope/StepContextAware.java @@ -15,14 +15,13 @@ */ package org.springframework.batch.execution.scope; -import org.springframework.batch.repeat.RepeatContext; -import org.springframework.core.AttributeAccessor; - /** - * Marker interface for beans to be injected with a {@link RepeatContext}. - * Useful for business logic implementations that want to store some state in - * the context, to communicate between iterations, or with an enclosing - * interceptor. + * Marker interface for beans to be injected with a {@link StepContext}. Useful + * for business logic implementations that want to store some state in the + * context, to communicate between iterations, or with an enclosing executor.
+ * + * A bean which is step scoped which also implements this interface will be + * injected with the context at the start of the bean lifecycle. * * @author Dave Syer * @@ -30,9 +29,10 @@ import org.springframework.core.AttributeAccessor; public interface StepContextAware { /** - * Callback for injection of {@link RepeatContext}. + * Callback for injection of {@link StepContext}. * - * @param context the current context supplied by framework. + * @param context + * the current context supplied by framework. */ - void setStepScopeContext(AttributeAccessor context); + void setStepContext(StepContext context); } diff --git a/execution/src/main/java/org/springframework/batch/execution/scope/StepScope.java b/execution/src/main/java/org/springframework/batch/execution/scope/StepScope.java index 5df676d80..016b97c6e 100644 --- a/execution/src/main/java/org/springframework/batch/execution/scope/StepScope.java +++ b/execution/src/main/java/org/springframework/batch/execution/scope/StepScope.java @@ -62,7 +62,7 @@ public class StepScope implements Scope, scopedObject = objectFactory.getObject(); context.setAttribute(name, scopedObject); if (scopedObject instanceof StepContextAware) { - ((StepContextAware) scopedObject).setStepScopeContext(context); + ((StepContextAware) scopedObject).setStepContext(context); } } diff --git a/execution/src/test/java/org/springframework/batch/execution/facade/BatchResourceFactoryBeanTests.java b/execution/src/test/java/org/springframework/batch/execution/facade/BatchResourceFactoryBeanTests.java index 428a3c23e..67aff9557 100644 --- a/execution/src/test/java/org/springframework/batch/execution/facade/BatchResourceFactoryBeanTests.java +++ b/execution/src/test/java/org/springframework/batch/execution/facade/BatchResourceFactoryBeanTests.java @@ -17,13 +17,22 @@ package org.springframework.batch.execution.facade; import java.io.File; +import java.io.IOException; +import java.text.SimpleDateFormat; import java.util.Calendar; import junit.framework.TestCase; +import org.springframework.batch.core.domain.JobExecution; +import org.springframework.batch.core.domain.JobInstance; +import org.springframework.batch.core.domain.StepExecution; +import org.springframework.batch.core.domain.StepInstance; +import org.springframework.batch.execution.runtime.ScheduledJobIdentifier; +import org.springframework.batch.execution.scope.SimpleStepContext; import org.springframework.core.io.ByteArrayResource; import org.springframework.core.io.DefaultResourceLoader; import org.springframework.core.io.Resource; +import org.springframework.util.StringUtils; /** * Unit tests for {@link BatchResourceFactoryBean} @@ -40,10 +49,11 @@ public class BatchResourceFactoryBeanTests extends TestCase { private BatchResourceFactoryBean resourceFactory = new BatchResourceFactoryBean(); private String rootDir = getRootDir(); - + private char pathsep = File.separatorChar; - - private String PATTERN_STRING = "/%BATCH_ROOT%"+pathsep+"%JOB_NAME%-%SCHEDULE_DATE%-%JOB_RUN%-%STREAM_NAME%"; + + private String PATTERN_STRING = "/%BATCH_ROOT%" + pathsep + + "%JOB_NAME%-%SCHEDULE_DATE%-%JOB_RUN%-%STREAM_NAME%"; /** * mock step context @@ -70,7 +80,7 @@ public class BatchResourceFactoryBeanTests extends TestCase { private String getRootDir() { String rootDir = System.getProperty("java.io.tmpdir"); assertNotNull(rootDir); - if (rootDir!=null && rootDir.endsWith(File.separator)) { + if (rootDir != null && rootDir.endsWith(File.separator)) { rootDir = rootDir.substring(0, rootDir.lastIndexOf(File.separator)); } return rootDir; @@ -80,16 +90,7 @@ public class BatchResourceFactoryBeanTests extends TestCase { * regular use with valid context and pattern provided */ public void testCreateFileName() throws Exception { - - Resource resource = (Resource) resourceFactory.getObject(); - - String returnedPath = resource.getFile().getAbsolutePath(); - - String absolutePath = new File("/"+rootDir+pathsep+"testJob-20070730-0-testStream").getAbsolutePath(); - - System.err.println(absolutePath); - System.err.println(returnedPath); - assertEquals(absolutePath, returnedPath); + doTestPathName("testJob-20070730-0-testStream"); } /** @@ -102,21 +103,13 @@ public class BatchResourceFactoryBeanTests extends TestCase { // set singleton to false so a new instance is returned. resourceFactory.setSingleton(false); - Resource resource = (Resource) resourceFactory.getObject(); - - String returnedPath = resource.getFile().getAbsolutePath(); - - String absolutePath = new File("/"+rootDir+pathsep+"job-20070730-0-testStream").getAbsolutePath(); - - System.err.println(absolutePath); - System.err.println(returnedPath); - assertEquals(absolutePath, returnedPath); + doTestPathName("job-20070730-0-testStream"); } - + public void testObjectType() throws Exception { assertEquals(Resource.class, resourceFactory.getObjectType()); } - + public void testNullFilePattern() throws Exception { resourceFactory = new BatchResourceFactoryBean(); resourceFactory.setSingleton(false); @@ -128,7 +121,7 @@ public class BatchResourceFactoryBeanTests extends TestCase { // expected } } - + public void testResoureLoaderAware() throws Exception { resourceFactory = new BatchResourceFactoryBean(); resourceFactory.setSingleton(false); @@ -142,4 +135,69 @@ public class BatchResourceFactoryBeanTests extends TestCase { assertTrue(resource.exists()); } + public void testStepContextAware() throws Exception { + + SimpleStepContext context = new SimpleStepContext(); + ScheduledJobIdentifier identifier = new ScheduledJobIdentifier("foo"); + identifier.setJobStream("stream"); + identifier.setJobRun(11); + identifier.setScheduleDate(new SimpleDateFormat("yyyyMMdd") + .parse("20070801")); + JobInstance job = new JobInstance(identifier); + JobExecution jobExecution = new JobExecution(job); + StepInstance step = new StepInstance(job, "bar"); + StepExecution stepExecution = new StepExecution(step, jobExecution); + context.setStepExecution(stepExecution); + resourceFactory.setStepContext(context); + + doTestPathName("foo-20070801-11-stream"); + + } + + public void testRootDirectoryEndsWithForwardSlash() throws Exception { + String rootDir = getRootDir(); + rootDir = StringUtils.replace(rootDir, File.separator, "/") + "/"; + resourceFactory.setRootDirectory(rootDir); + doTestPathName("testJob-20070730-0-testStream"); + } + + public void testRootDirectoryEndsWithBackSlash() throws Exception { + String rootDir = getRootDir(); + rootDir = StringUtils.replace(rootDir, File.separator, "\\") + "\\"; + resourceFactory.setRootDirectory(rootDir); + doTestPathName("testJob-20070730-0-testStream"); + } + + public void testDateFormatPattern() throws Exception { + resourceFactory.setDateFormatPattern("ddMMyyyy"); + + SimpleStepContext context = new SimpleStepContext(); + ScheduledJobIdentifier identifier = new ScheduledJobIdentifier("foo"); + identifier.setJobStream("stream"); + identifier.setJobRun(11); + identifier.setScheduleDate(new SimpleDateFormat("yyyyMMdd") + .parse("20070802")); + JobInstance job = new JobInstance(identifier); + JobExecution jobExecution = new JobExecution(job); + StepInstance step = new StepInstance(job, "bar"); + StepExecution stepExecution = new StepExecution(step, jobExecution); + context.setStepExecution(stepExecution); + resourceFactory.setStepContext(context); + + doTestPathName("foo-02082007-11-stream"); + } + + private void doTestPathName(String filename) throws Exception, IOException { + Resource resource = (Resource) resourceFactory.getObject(); + + String returnedPath = resource.getFile().getAbsolutePath(); + + String absolutePath = new File("/" + rootDir + pathsep + + filename).getAbsolutePath(); + + System.err.println(absolutePath); + System.err.println(returnedPath); + assertEquals(absolutePath, returnedPath); + } + } diff --git a/execution/src/test/java/org/springframework/batch/execution/scope/StepContextAwareStepScopeTests.java b/execution/src/test/java/org/springframework/batch/execution/scope/StepContextAwareStepScopeTests.java index ec3f6e2ac..69fc0709f 100644 --- a/execution/src/test/java/org/springframework/batch/execution/scope/StepContextAwareStepScopeTests.java +++ b/execution/src/test/java/org/springframework/batch/execution/scope/StepContextAwareStepScopeTests.java @@ -66,15 +66,17 @@ public class StepContextAwareStepScopeTests extends TestCase { assertEquals(context, bean.context); } -// public void testScopedBeanWithInner() throws Exception { -// StepSynchronizationManager.open(); -// ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("scope-tests.xml", getClass()); -// TestBean bean = ((TestBean) applicationContext.getBean("inner")).child; -// assertNotNull(bean); -// assertEquals("bar", bean.name); -// StepSynchronizationManager.close(); -// assertEquals(1, list.size()); -// } + public void testScopedBeanWithInner() throws Exception { + StepSynchronizationManager.open(); + ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext( + "scope-tests.xml", getClass()); + TestBean bean = ((TestBean) applicationContext.getBean("inner")).child; + assertNotNull(bean); + assertEquals("bar", bean.name); + StepSynchronizationManager.close(); + // TODO: Still a bug in Spring Core? Preventing destroy method from being called in inner bean. + // assertEquals(1, list.size()); + } public static class TestBean { String name; @@ -92,7 +94,7 @@ public class StepContextAwareStepScopeTests extends TestCase { public static class TestBeanAware extends TestBean implements StepContextAware { AttributeAccessor context; - public void setStepScopeContext(AttributeAccessor context) { + public void setStepContext(StepContext context) { this.context = context; } }