diff --git a/spring-batch-test/.classpath b/spring-batch-test/.classpath
new file mode 100755
index 000000000..638acf57b
--- /dev/null
+++ b/spring-batch-test/.classpath
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/spring-batch-test/.project b/spring-batch-test/.project
new file mode 100755
index 000000000..e468810c7
--- /dev/null
+++ b/spring-batch-test/.project
@@ -0,0 +1,29 @@
+
+
+ spring-batch-test
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ org.devzuz.q.maven.jdt.core.mavenIncrementalBuilder
+
+
+
+
+ org.springframework.ide.eclipse.core.springbuilder
+
+
+
+
+
+ org.springframework.ide.eclipse.core.springnature
+ org.eclipse.jdt.core.javanature
+ org.devzuz.q.maven.jdt.core.mavenNature
+
+
diff --git a/spring-batch-test/.springBeans b/spring-batch-test/.springBeans
new file mode 100644
index 000000000..6151e7570
--- /dev/null
+++ b/spring-batch-test/.springBeans
@@ -0,0 +1,30 @@
+
+
+ 1
+
+
+
+
+
+
+ src/test/resources/data-source-context-init.xml
+ src/test/resources/data-source-context.xml
+ src/test/resources/simple-job-launcher-context.xml
+ src/test/resources/jobs/sampleJob.xml
+ src/test/resources/org/springframework/batch/sample/config/common-context.xml
+
+
+
+
+ true
+ false
+
+ src/test/resources/data-source-context.xml
+ src/test/resources/data-source-context-init.xml
+ src/test/resources/jobs/sampleJob.xml
+ src/test/resources/org/springframework/batch/sample/config/common-context.xml
+ src/test/resources/simple-job-launcher-context.xml
+
+
+
+
diff --git a/spring-batch-test/pom.xml b/spring-batch-test/pom.xml
new file mode 100755
index 000000000..d8cf3917a
--- /dev/null
+++ b/spring-batch-test/pom.xml
@@ -0,0 +1,86 @@
+
+
+
+ spring-batch
+ org.springframework.batch
+ 2.0.0.CI-SNAPSHOT
+ ..
+
+ 4.0.0
+ spring-batch-test
+ Core
+ 2.0.0.CI-SNAPSHOT
+ Domain for batch job testing
+
+
+
+ com.springsource.bundlor
+ com.springsource.bundlor.maven
+
+
+
+
+
+
+ clover
+
+
+
+
+ maven-clover-plugin
+
+
+
+
+
+
+ maven-clover-plugin
+
+
+
+
+
+
+
+ org.springframework.batch
+ org.springframework.batch.core
+ ${project.version}
+
+
+ junit
+ junit
+
+
+ org.springframework
+ spring-test
+
+
+ commons-io
+ commons-io
+
+
+ org.springframework
+ spring-jdbc
+
+
+ commons-dbcp
+ commons-dbcp
+
+
+ hsqldb
+ hsqldb
+
+
+ commons-collections
+ commons-collections
+
+
+
+
+
+ org.codehaus.mojo
+ emma-maven-plugin
+
+
+
+
\ No newline at end of file
diff --git a/spring-batch-test/src/main/java/org/springframework/batch/test/AbstractSimpleJobTests.java b/spring-batch-test/src/main/java/org/springframework/batch/test/AbstractSimpleJobTests.java
new file mode 100755
index 000000000..fb1fae886
--- /dev/null
+++ b/spring-batch-test/src/main/java/org/springframework/batch/test/AbstractSimpleJobTests.java
@@ -0,0 +1,154 @@
+package org.springframework.batch.test;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.batch.core.JobExecution;
+import org.springframework.batch.core.JobParameter;
+import org.springframework.batch.core.JobParameters;
+import org.springframework.batch.core.Step;
+import org.springframework.batch.core.job.SimpleJob;
+import org.springframework.batch.core.launch.JobLauncher;
+import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException;
+import org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException;
+import org.springframework.batch.core.repository.JobRepository;
+import org.springframework.batch.core.repository.JobRestartException;
+import org.springframework.beans.factory.annotation.Autowired;
+
+/**
+ * Base class for testing batch jobs using the SimpleJob implementation.
+ * It provides methods for launching a Job, or individual Steps within a Job on their own,
+ * allowing for end to end testing of individual steps, without having to run every step
+ * in the job. Any test classes inheriting from this class should make sure they are part
+ * of an ApplicationContext, which is generally expected to be done as part of the Spring
+ * test framework. Furthermore, the ApplicationContext in which it is a part of is expected
+ * to have one {@link JobLauncher}, {@link JobRepository}, and a single Job implementation.
+ * It should be noted that using any of the methods that don't conain {@link JobParameters}
+ * in their signature, will result in one being created with the current system time as a
+ * parameter.
+ *
+ * @author Lucas Ward
+ * @author Dan Garrette
+ * @since 2.0
+ */
+public abstract class AbstractSimpleJobTests {
+
+ /** Logger */
+ protected final Log logger = LogFactory.getLog(getClass());
+
+ private JobLauncher launcher;
+ private JobRepository jobRepository;
+ private SimpleJob job;
+ private StepRunner stepRunner;
+
+ private Map stepMap = new HashMap();
+ private List stepList = new ArrayList();
+
+ @Autowired
+ public void setLauncher(JobLauncher bootstrap) {
+ this.launcher = bootstrap;
+ }
+
+ @Autowired
+ public void setJobRepository(JobRepository jobRepository) {
+ this.jobRepository = jobRepository;
+ }
+
+ @Autowired
+ public void setJob(SimpleJob job) {
+ this.job = job;
+
+ for (Step step : job.getSteps()) {
+ stepMap.put(step.getName(), step);
+ stepList.add(step);
+ }
+ }
+
+ public StepRunner getStepRunner() {
+ if(stepRunner == null){
+ stepRunner = new StepRunner(launcher, jobRepository);
+ }
+ return stepRunner;
+ }
+
+ public SimpleJob getJob() {
+ return job;
+ }
+
+ /**
+ * Public getter for the launcher.
+ *
+ * @return the launcher
+ */
+ protected JobLauncher getLauncher() {
+ return launcher;
+ }
+
+ public Step getStep(String stepName){
+
+ if(!stepMap.containsKey(stepName)){
+ throw new IllegalStateException("No Step found with name: [" + stepName + "]");
+ }
+ return stepMap.get(stepName);
+ }
+ /**
+ * Launch the entire job, including all steps, in order.
+ *
+ * @return JobExecution, so that the test may validate the exit status
+ */
+ public JobExecution launchJob() {
+ return this.launchJob(this.makeUniqueJobParameters());
+ }
+
+ /**
+ * Launch the entire job, including all steps, in order.
+ *
+ * @param jobParameters
+ * @return JobExecution, so that the test may validate the exit status
+ */
+ public JobExecution launchJob(JobParameters jobParameters) {
+ try {
+ return getLauncher().run(job, jobParameters);
+ } catch (JobExecutionAlreadyRunningException e) {
+ throw new RuntimeException(e);
+ } catch (JobRestartException e) {
+ throw new RuntimeException(e);
+ } catch (JobInstanceAlreadyCompleteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Launch just the specified step in the job.
+ *
+ * @param stepName
+ */
+ public JobExecution launchStep(String stepName) {
+ return getStepRunner().launchStep(getStep(stepName));
+ }
+
+ /**
+ * Launch just the specified step in the job.
+ *
+ * @param stepName
+ * @param jobParameters
+ */
+ public JobExecution launchStep(String stepName, JobParameters jobParameters) {
+ return getStepRunner().launchStep(getStep(stepName), jobParameters);
+ }
+
+ /**
+ * @return a new JobParameters object containing only a parameter for the
+ * current timestamp, to ensure that the job instance will be unique
+ */
+ private JobParameters makeUniqueJobParameters() {
+ Map parameters = new HashMap();
+ parameters.put("timestamp", new JobParameter(new Date().getTime()));
+ return new JobParameters(parameters);
+ }
+}
diff --git a/spring-batch-test/src/main/java/org/springframework/batch/test/StepRunner.java b/spring-batch-test/src/main/java/org/springframework/batch/test/StepRunner.java
new file mode 100755
index 000000000..dfb50bb2b
--- /dev/null
+++ b/spring-batch-test/src/main/java/org/springframework/batch/test/StepRunner.java
@@ -0,0 +1,117 @@
+package org.springframework.batch.test;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.batch.core.Job;
+import org.springframework.batch.core.JobExecution;
+import org.springframework.batch.core.JobParameter;
+import org.springframework.batch.core.JobParameters;
+import org.springframework.batch.core.Step;
+import org.springframework.batch.core.job.SimpleJob;
+import org.springframework.batch.core.launch.JobLauncher;
+import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException;
+import org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException;
+import org.springframework.batch.core.repository.JobRepository;
+import org.springframework.batch.core.repository.JobRestartException;
+
+/**
+ * Utility class for executing steps outside of a {@link Job}. This is useful
+ * in end to end testing in order to allow for the testing of a step individually
+ * without running every Step in a job.
+ *
+ *
+ * - launchStep(Step step): Launch the step with new parameters each time. (The current system
+ * time will be used)
+ *
- launchStep(Step step, JobParameters jobParameters): Launch the specified step with the provided
+ * JobParameters. This may be useful if your step requires a certain parameter during runtime.
+ *
+ *
+ * It should be noted that any checked exceptions encountered while running the Step will wrapped with
+ * RuntimeException. Any checked exception thrown will be due to a framework error, not the logic of the
+ * step, and thus requiring a throws declaration in clients of this class is unnecessary.
+ *
+ * @author Dan Garrette
+ * @author Lucas Ward
+ * @since 2.0
+ * @see SimpleJob
+ */
+public class StepRunner{
+
+ /** Logger */
+ protected final Log logger = LogFactory.getLog(getClass());
+
+ private JobLauncher launcher;
+ private JobRepository jobRepository;
+
+ public StepRunner(JobLauncher launcher, JobRepository jobRepository) {
+ this.launcher = launcher;
+ this.jobRepository = jobRepository;
+ }
+
+ /**
+ * Launcher
+ *
+ * @param stepName
+ */
+ public JobExecution launchStep(Step step) {
+ return this.launchStep(step, this.makeUniqueJobParameters());
+ }
+
+ /**
+ * Launch just the specified step in the job.
+ *
+ * @param stepName
+ * @param jobParameters
+ */
+ public JobExecution launchStep(Step step, JobParameters jobParameters) {
+ //
+ // Create a fake job
+ //
+ SimpleJob job = new SimpleJob();
+ job.setName("TestJob");
+ job.setJobRepository(this.jobRepository);
+
+ List stepsToExecute = new ArrayList();
+ stepsToExecute.add(step);
+ job.setSteps(stepsToExecute);
+
+ //
+ // Launch the job
+ //
+ return this.launchJob(job, jobParameters);
+ }
+
+ /**
+ * Launch the given job
+ *
+ * @param job
+ * @param jobParameters
+ */
+ private JobExecution launchJob(Job job, JobParameters jobParameters) {
+ try {
+ return this.launcher.run(job, jobParameters);
+ } catch (JobExecutionAlreadyRunningException e) {
+ throw new RuntimeException(e);
+ } catch (JobRestartException e) {
+ throw new RuntimeException(e);
+ } catch (JobInstanceAlreadyCompleteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * @return a new JobParameters object containing only a parameter for the
+ * current timestamp, to ensure that the job instance will be unique
+ */
+ private JobParameters makeUniqueJobParameters() {
+ Map parameters = new HashMap();
+ parameters.put("timestamp", new JobParameter(new Date().getTime()));
+ return new JobParameters(parameters);
+ }
+}
diff --git a/spring-batch-test/src/test/java/org/springframework/batch/test/SampleJobTests.java b/spring-batch-test/src/test/java/org/springframework/batch/test/SampleJobTests.java
new file mode 100755
index 000000000..c9b1e867e
--- /dev/null
+++ b/spring-batch-test/src/test/java/org/springframework/batch/test/SampleJobTests.java
@@ -0,0 +1,63 @@
+package org.springframework.batch.test;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.batch.core.BatchStatus;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.jdbc.core.simple.SimpleJdbcTemplate;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration(locations = { "/simple-job-launcher-context.xml", "/jobs/sampleJob.xml" })
+public class SampleJobTests extends AbstractSimpleJobTests {
+
+ private SimpleJdbcTemplate jdbcTemplate;
+
+ @Autowired
+ public void setJdbcTemplate(SimpleJdbcTemplate jdbcTemplate) {
+ this.jdbcTemplate = jdbcTemplate;
+ }
+
+ @Before
+ public void setUp() {
+ this.jdbcTemplate.update("create table TESTS (ID integer, NAME varchar(40))");
+ }
+
+ @After
+ public void tearDown() {
+ this.jdbcTemplate.update("drop table TESTS");
+ }
+
+ @Test
+ public void test1() {
+ assertEquals(BatchStatus.COMPLETED, this.launchStep("step1").getStatus());
+ this.verifyTasklet(1);
+ }
+
+ private void verifyTasklet(int id) {
+ assertEquals(id, jdbcTemplate.queryForInt("SELECT ID from TESTS where NAME = 'SampleTasklet" + id + "'"));
+ }
+
+ @Test
+ public void testJob() {
+ assertEquals(BatchStatus.COMPLETED,this.launchJob().getStatus());
+ this.verifyTasklet(1);
+ this.verifyTasklet(2);
+ }
+
+ @Test(expected=IllegalStateException.class)
+ public void voidTestNonExistentStep(){
+ launchStep("nonExistent");
+ }
+
+ @Test
+ public void test2() {
+ assertEquals(BatchStatus.COMPLETED, this.launchStep("step2").getStatus());
+ this.verifyTasklet(2);
+ }
+}
diff --git a/spring-batch-test/src/test/java/org/springframework/batch/test/SampleStepTests.java b/spring-batch-test/src/test/java/org/springframework/batch/test/SampleStepTests.java
new file mode 100755
index 000000000..3af16a91c
--- /dev/null
+++ b/spring-batch-test/src/test/java/org/springframework/batch/test/SampleStepTests.java
@@ -0,0 +1,60 @@
+package org.springframework.batch.test;
+
+import static org.junit.Assert.*;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.batch.core.BatchStatus;
+import org.springframework.batch.core.Step;
+import org.springframework.batch.core.launch.JobLauncher;
+import org.springframework.batch.core.repository.JobRepository;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.jdbc.core.simple.SimpleJdbcTemplate;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration(locations = { "/simple-job-launcher-context.xml", "/jobs/sampleJob.xml" })
+public class SampleStepTests implements ApplicationContextAware{
+
+ @Autowired
+ private SimpleJdbcTemplate jdbcTemplate;
+
+ private StepRunner stepRunner;
+ private ApplicationContext context;
+
+ @Autowired
+ private JobLauncher jobLauncher;
+
+ @Autowired
+ private JobRepository jobRepository;
+
+ @Before
+ public void setUp() {
+ jdbcTemplate.update("create table TESTS (ID integer, NAME varchar(40))");
+ stepRunner = new StepRunner(jobLauncher, jobRepository);
+ }
+
+ @After
+ public void tearDown() {
+ this.jdbcTemplate.update("drop table TESTS");
+ }
+
+ @Test
+ public void testTasklet() {
+ Step step = (Step)context.getBean("step2");
+ assertEquals(BatchStatus.COMPLETED, stepRunner.launchStep(step).getStatus());
+ assertEquals(2, jdbcTemplate.queryForInt("SELECT ID from TESTS where NAME = 'SampleTasklet2'"));
+ }
+
+ public void setApplicationContext(ApplicationContext applicationContext)
+ throws BeansException {
+ this.context = applicationContext;
+ }
+
+}
diff --git a/spring-batch-test/src/test/java/org/springframework/batch/test/common/LogAdvice.java b/spring-batch-test/src/test/java/org/springframework/batch/test/common/LogAdvice.java
new file mode 100755
index 000000000..146045bad
--- /dev/null
+++ b/spring-batch-test/src/test/java/org/springframework/batch/test/common/LogAdvice.java
@@ -0,0 +1,56 @@
+/*
+ * 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.test.common;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.aspectj.lang.JoinPoint;
+
+
+/**
+ * Wraps calls for 'Processing' methods which output a single Object to write
+ * the string representation of the object to the log.
+ *
+ * @author Lucas Ward
+ */
+public class LogAdvice {
+
+ private static Log log = LogFactory.getLog(LogAdvice.class);
+
+ /*
+ * Wraps original method and adds logging both before and after method
+ */
+ public void doBasicLogging(JoinPoint pjp) throws Throwable {
+ Object[] args = pjp.getArgs();
+ StringBuffer output = new StringBuffer();
+
+ output.append(pjp.getTarget().getClass().getName()).append(": ");
+ output.append(pjp.toShortString()).append(": ");
+
+ for (Object arg : args) {
+ output.append(arg).append(" ");
+ }
+
+
+ log.info("Basic: " + output.toString());
+ }
+
+ public void doStronglyTypedLogging(Object item){
+ log.info("Processed: " + item);
+ }
+
+}
diff --git a/spring-batch-test/src/test/java/org/springframework/batch/test/jmx/JobExecutionNotificationPublisher.java b/spring-batch-test/src/test/java/org/springframework/batch/test/jmx/JobExecutionNotificationPublisher.java
new file mode 100755
index 000000000..1fe1d04b5
--- /dev/null
+++ b/spring-batch-test/src/test/java/org/springframework/batch/test/jmx/JobExecutionNotificationPublisher.java
@@ -0,0 +1,86 @@
+/*
+ * 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.test.jmx;
+
+import javax.management.Notification;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.context.ApplicationEvent;
+import org.springframework.context.ApplicationListener;
+import org.springframework.jmx.export.notification.NotificationPublisher;
+import org.springframework.jmx.export.notification.NotificationPublisherAware;
+
+/**
+ * JMX notification broadcaster
+ *
+ * @author Dave Syer
+ * @since 1.0
+ */
+public class JobExecutionNotificationPublisher implements ApplicationListener, NotificationPublisherAware {
+
+ protected static final Log logger = LogFactory.getLog(JobExecutionNotificationPublisher.class);
+
+ private NotificationPublisher notificationPublisher;
+
+ private int notificationCount = 0;
+
+ /**
+ * Injection setter.
+ *
+ * @see org.springframework.jmx.export.notification.NotificationPublisherAware#setNotificationPublisher(org.springframework.jmx.export.notification.NotificationPublisher)
+ */
+ public void setNotificationPublisher(NotificationPublisher notificationPublisher) {
+ this.notificationPublisher = notificationPublisher;
+ }
+
+ /**
+ * If the event is a {@link SimpleMessageApplicationEvent} for open and
+ * close we log the event at INFO level and send a JMX notification if we
+ * are also an MBean.
+ *
+ * @see ApplicationListener#onApplicationEvent(ApplicationEvent)
+ */
+ public void onApplicationEvent(ApplicationEvent applicationEvent) {
+ if (applicationEvent instanceof SimpleMessageApplicationEvent) {
+ String message = applicationEvent.toString();
+ logger.info(message);
+ publish(message);
+ }
+ }
+
+ /**
+ * Publish the provided message to an external listener if there is one.
+ *
+ * @param message the message to publish
+ */
+ private void publish(String message) {
+ if (notificationPublisher != null) {
+ Notification notification = new Notification("JobExecutionApplicationEvent", this, notificationCount++,
+ message);
+ /*
+ * We can't create a notification with a null source, but we can set
+ * it to null after creation(!). We want it to be null so that
+ * Spring will replace it automatically with the ObjectName (in
+ * ModelMBeanNotificationPublisher).
+ */
+ notification.setSource(null);
+ notificationPublisher.sendNotification(notification);
+ }
+ }
+
+}
diff --git a/spring-batch-test/src/test/java/org/springframework/batch/test/jmx/SimpleMessageApplicationEvent.java b/spring-batch-test/src/test/java/org/springframework/batch/test/jmx/SimpleMessageApplicationEvent.java
new file mode 100755
index 000000000..ba1a9ea64
--- /dev/null
+++ b/spring-batch-test/src/test/java/org/springframework/batch/test/jmx/SimpleMessageApplicationEvent.java
@@ -0,0 +1,25 @@
+package org.springframework.batch.test.jmx;
+
+import org.springframework.context.ApplicationEvent;
+
+/**
+ * @author Dave Syer
+ *
+ */
+public class SimpleMessageApplicationEvent extends ApplicationEvent {
+
+ private String message;
+
+ public SimpleMessageApplicationEvent(Object source, String message) {
+ super(source);
+ this.message = message;
+ }
+
+ /* (non-Javadoc)
+ * @see java.util.EventObject#toString()
+ */
+ public String toString() {
+ return "message=["+message+"], " + super.toString();
+ }
+
+}
\ No newline at end of file
diff --git a/spring-batch-test/src/test/java/org/springframework/batch/test/jmx/StepExecutionApplicationEventAdvice.java b/spring-batch-test/src/test/java/org/springframework/batch/test/jmx/StepExecutionApplicationEventAdvice.java
new file mode 100755
index 000000000..815552b00
--- /dev/null
+++ b/spring-batch-test/src/test/java/org/springframework/batch/test/jmx/StepExecutionApplicationEventAdvice.java
@@ -0,0 +1,66 @@
+/*
+ * 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.test.jmx;
+
+import org.aspectj.lang.JoinPoint;
+import org.springframework.batch.core.StepExecution;
+import org.springframework.context.ApplicationEvent;
+import org.springframework.context.ApplicationEventPublisher;
+import org.springframework.context.ApplicationEventPublisherAware;
+
+/**
+ * Wraps calls for methods taking {@link StepExecution} as an argument and
+ * publishes notifications in the form of {@link ApplicationEvent}.
+ *
+ * @author Dave Syer
+ */
+public class StepExecutionApplicationEventAdvice implements ApplicationEventPublisherAware {
+
+ private ApplicationEventPublisher applicationEventPublisher;
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.context.ApplicationEventPublisherAware#setApplicationEventPublisher(org.springframework.context.ApplicationEventPublisher)
+ */
+ public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
+ this.applicationEventPublisher = applicationEventPublisher;
+ }
+
+ public void before(JoinPoint jp, StepExecution stepExecution) {
+ String msg = "Before: " + jp.toShortString() + " with: " + stepExecution;
+ publish(jp.getTarget(), msg);
+ }
+
+ public void after(JoinPoint jp, StepExecution stepExecution) {
+ String msg = "After: " + jp.toShortString() + " with: " + stepExecution;
+ publish(jp.getTarget(), msg);
+ }
+
+ public void onError(JoinPoint jp, StepExecution stepExecution, Throwable t) {
+ String msg = "Error in: " + jp.toShortString() + " with: " + stepExecution + " (" + t.getClass() + ":" + t.getMessage() + ")";
+ publish(jp.getTarget(), msg);
+ }
+
+ /*
+ * Publish a {@link SimpleMessageApplicationEvent} with the given
+ * parameters.
+ */
+ private void publish(Object source, String message) {
+ applicationEventPublisher.publishEvent(new SimpleMessageApplicationEvent(source, message));
+ }
+
+}
diff --git a/spring-batch-test/src/test/java/org/springframework/batch/test/sample/SampleTasklet.java b/spring-batch-test/src/test/java/org/springframework/batch/test/sample/SampleTasklet.java
new file mode 100755
index 000000000..663f5e6a2
--- /dev/null
+++ b/spring-batch-test/src/test/java/org/springframework/batch/test/sample/SampleTasklet.java
@@ -0,0 +1,26 @@
+package org.springframework.batch.test.sample;
+
+import org.springframework.batch.core.StepContribution;
+import org.springframework.batch.core.step.tasklet.Tasklet;
+import org.springframework.batch.repeat.ExitStatus;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.AttributeAccessor;
+import org.springframework.jdbc.core.simple.SimpleJdbcTemplate;
+
+public class SampleTasklet implements Tasklet {
+
+ @Autowired
+ private SimpleJdbcTemplate jdbcTemplate;
+ private int id = 0;
+
+ public SampleTasklet(int id) {
+ this.id = id;
+ }
+
+ public ExitStatus execute(StepContribution contribution, AttributeAccessor attributes) throws Exception {
+ System.err.println("SampleTasklet1.execute()");
+ this.jdbcTemplate.update("insert into TESTS(ID, NAME) values (?, 'SampleTasklet" + id + "')", id);
+
+ return ExitStatus.FINISHED;
+ }
+}
diff --git a/spring-batch-test/src/test/java/test/jdbc/datasource/DataSourceInitializer.java b/spring-batch-test/src/test/java/test/jdbc/datasource/DataSourceInitializer.java
new file mode 100755
index 000000000..ccfdef88f
--- /dev/null
+++ b/spring-batch-test/src/test/java/test/jdbc/datasource/DataSourceInitializer.java
@@ -0,0 +1,191 @@
+/*
+ * 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 test.jdbc.datasource;
+
+import java.io.IOException;
+import java.util.List;
+
+import javax.sql.DataSource;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.beans.factory.BeanInitializationException;
+import org.springframework.beans.factory.DisposableBean;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+import org.springframework.core.io.Resource;
+import org.springframework.dao.DataAccessException;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.datasource.DataSourceTransactionManager;
+import org.springframework.transaction.TransactionStatus;
+import org.springframework.transaction.support.TransactionCallback;
+import org.springframework.transaction.support.TransactionTemplate;
+import org.springframework.util.Assert;
+import org.springframework.util.ClassUtils;
+import org.springframework.util.StringUtils;
+
+/**
+ * Wrapper for a {@link DataSource} that can run scripts on start up and shut
+ * down. Us as a bean definition
+ *
+ * Run this class to initialize a database in a running server process.
+ * Make sure the server is running first by launching the "hsql-server" from the
+ * hsql.server project. Then you can right click in Eclipse and
+ * Run As -> Java Application. Do the same any time you want to wipe the
+ * database and start again.
+ *
+ * @author Dave Syer
+ *
+ */
+public class DataSourceInitializer implements InitializingBean, DisposableBean {
+
+ private static final Log logger = LogFactory.getLog(DataSourceInitializer.class);
+
+ private Resource[] initScripts;
+
+ private Resource[] destroyScripts;
+
+ private DataSource dataSource;
+
+ private boolean ignoreFailedDrop = true;
+
+ private static boolean initialized = false;
+
+ /**
+ * Main method as convenient entry point.
+ *
+ * @param args
+ */
+ public static void main(String... args) {
+ new ClassPathXmlApplicationContext(ClassUtils.addResourcePathToPackagePath(DataSourceInitializer.class,
+ DataSourceInitializer.class.getSimpleName() + "-context.xml"));
+ }
+
+ /**
+ * @throws Throwable
+ * @see java.lang.Object#finalize()
+ */
+ protected void finalize() throws Throwable {
+ super.finalize();
+ initialized = false;
+ logger.debug("finalize called");
+ }
+
+ public void destroy() {
+ if (destroyScripts==null) return;
+ for (int i = 0; i < destroyScripts.length; i++) {
+ Resource destroyScript = initScripts[i];
+ try {
+ doExecuteScript(destroyScript);
+ }
+ catch (Exception e) {
+ if (logger.isDebugEnabled()) {
+ logger.warn("Could not execute destroy script [" + destroyScript + "]", e);
+ }
+ else {
+ logger.warn("Could not execute destroy script [" + destroyScript + "]");
+ }
+ }
+ }
+ }
+
+ public void afterPropertiesSet() throws Exception {
+ Assert.notNull(dataSource);
+ initialize();
+ }
+
+ private void initialize() {
+ if (!initialized) {
+ destroy();
+ if (initScripts != null) {
+ for (int i = 0; i < initScripts.length; i++) {
+ Resource initScript = initScripts[i];
+ doExecuteScript(initScript);
+ }
+ }
+ initialized = true;
+ }
+ }
+
+ private void doExecuteScript(final Resource scriptResource) {
+ if (scriptResource == null || !scriptResource.exists())
+ return;
+ TransactionTemplate transactionTemplate = new TransactionTemplate(new DataSourceTransactionManager(dataSource));
+ transactionTemplate.execute(new TransactionCallback() {
+
+ @SuppressWarnings("unchecked")
+ public Object doInTransaction(TransactionStatus status) {
+ JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
+ String[] scripts;
+ try {
+ scripts = StringUtils.delimitedListToStringArray(stripComments(IOUtils.readLines(scriptResource
+ .getInputStream())), ";");
+ }
+ catch (IOException e) {
+ throw new BeanInitializationException("Cannot load script from [" + scriptResource + "]", e);
+ }
+ for (int i = 0; i < scripts.length; i++) {
+ String script = scripts[i].trim();
+ if (StringUtils.hasText(script)) {
+ try {
+ jdbcTemplate.execute(script);
+ }
+ catch (DataAccessException e) {
+ if (ignoreFailedDrop && script.toLowerCase().startsWith("drop")) {
+ logger.debug("DROP script failed (ignoring): " + script);
+ }
+ else {
+ throw e;
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ });
+
+ }
+
+ private String stripComments(List list) {
+ StringBuffer buffer = new StringBuffer();
+ for (String line : list) {
+ if (!line.startsWith("//") && !line.startsWith("--")) {
+ buffer.append(line + "\n");
+ }
+ }
+ return buffer.toString();
+ }
+
+ public void setInitScripts(Resource[] initScripts) {
+ this.initScripts = initScripts;
+ }
+
+ public void setDestroyScripts(Resource[] destroyScripts) {
+ this.destroyScripts = destroyScripts;
+ }
+
+ public void setDataSource(DataSource dataSource) {
+ this.dataSource = dataSource;
+ }
+
+ public void setIgnoreFailedDrop(boolean ignoreFailedDrop) {
+ this.ignoreFailedDrop = ignoreFailedDrop;
+ }
+
+}
diff --git a/spring-batch-test/src/test/resources/batch-hsql.properties b/spring-batch-test/src/test/resources/batch-hsql.properties
new file mode 100755
index 000000000..7f28d4f4a
--- /dev/null
+++ b/spring-batch-test/src/test/resources/batch-hsql.properties
@@ -0,0 +1,21 @@
+# Placeholders batch.*
+# for HSQLDB:
+batch.jdbc.driver=org.hsqldb.jdbcDriver
+batch.jdbc.url=jdbc:hsqldb:mem:testdb;sql.enforce_strict_size=true
+# use this one for a separate server process so you can inspect the results
+# (or add it to system properties with -D to override at run time).
+# batch.jdbc.url=jdbc:hsqldb:hsql://localhost:9005/samples
+batch.jdbc.user=sa
+batch.jdbc.password=
+batch.schema=
+batch.jndi.name=
+batch.naming.factory.initial=
+batch.naming.provider.url=
+batch.schema.script=schema-hsqldb.sql
+batch.business.schema.script=business-schema-hsqldb.sql
+batch.database.incrementer.class=org.springframework.jdbc.support.incrementer.HsqlMaxValueIncrementer
+batch.lob.handler.class=org.springframework.jdbc.support.lob.DefaultLobHandler
+
+# Bean Properties for override
+# when not using sequences:
+incrementerParent.columnName=ID
diff --git a/spring-batch-test/src/test/resources/data-source-context-init.xml b/spring-batch-test/src/test/resources/data-source-context-init.xml
new file mode 100755
index 000000000..d6103fd9c
--- /dev/null
+++ b/spring-batch-test/src/test/resources/data-source-context-init.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+ ${batch.schema.script}
+ ${batch.business.schema.script}
+
+
+
+
+
\ No newline at end of file
diff --git a/spring-batch-test/src/test/resources/data-source-context.xml b/spring-batch-test/src/test/resources/data-source-context.xml
new file mode 100755
index 000000000..f4e0730cb
--- /dev/null
+++ b/spring-batch-test/src/test/resources/data-source-context.xml
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ environment
+
+
+ environment
+
+
+
+ hsql
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/spring-batch-test/src/test/resources/jobs/sampleJob.xml b/spring-batch-test/src/test/resources/jobs/sampleJob.xml
new file mode 100755
index 000000000..e569d7db3
--- /dev/null
+++ b/spring-batch-test/src/test/resources/jobs/sampleJob.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/spring-batch-test/src/test/resources/org/springframework/batch/sample/config/common-context.xml b/spring-batch-test/src/test/resources/org/springframework/batch/sample/config/common-context.xml
new file mode 100755
index 000000000..366f8c8aa
--- /dev/null
+++ b/spring-batch-test/src/test/resources/org/springframework/batch/sample/config/common-context.xml
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/spring-batch-test/src/test/resources/simple-job-launcher-context.xml b/spring-batch-test/src/test/resources/simple-job-launcher-context.xml
new file mode 100755
index 000000000..e9405da86
--- /dev/null
+++ b/spring-batch-test/src/test/resources/simple-job-launcher-context.xml
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file