diff --git a/spring-batch-execution/src/main/java/org/springframework/batch/execution/bootstrap/support/ExportedJobLauncher.java b/spring-batch-execution/src/main/java/org/springframework/batch/execution/bootstrap/support/ExportedJobLauncher.java
index 2a72da8af..655d11ce4 100644
--- a/spring-batch-execution/src/main/java/org/springframework/batch/execution/bootstrap/support/ExportedJobLauncher.java
+++ b/spring-batch-execution/src/main/java/org/springframework/batch/execution/bootstrap/support/ExportedJobLauncher.java
@@ -39,24 +39,27 @@ public interface ExportedJobLauncher {
* @param name the name of the job to launch
* @return a representation of the {@link JobExecution} returned by a
* {@link JobLauncher}.
- *
- * @see #run()
*/
String run(String name);
/**
- * Stop all running jobs.
+ * Launch a job with the given name and parameters.
*
- * @see JobLauncher#stop()
+ * @param name the name of the job to launch
+ * @return a representation of the {@link JobExecution} returned by a
+ * {@link JobLauncher}.
+ */
+ String run(String name, String params);
+
+ /**
+ * Stop all running jobs.
*/
void stop();
/**
- * Enquire if any jobs are still running.
+ * Enquire if any jobs launched here are still running.
*
* @return true if any jobs are running.
- *
- * @see JobLauncher#isRunning()
*/
boolean isRunning();
diff --git a/spring-batch-execution/src/main/java/org/springframework/batch/execution/bootstrap/support/JobPropertyEditor.java b/spring-batch-execution/src/main/java/org/springframework/batch/execution/bootstrap/support/JobPropertyEditor.java
new file mode 100644
index 000000000..2a35112d5
--- /dev/null
+++ b/spring-batch-execution/src/main/java/org/springframework/batch/execution/bootstrap/support/JobPropertyEditor.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2006-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.batch.execution.bootstrap.support;
+
+import java.beans.PropertyEditor;
+import java.beans.PropertyEditorSupport;
+
+import org.springframework.batch.core.domain.Job;
+import org.springframework.batch.core.domain.JobLocator;
+import org.springframework.batch.core.domain.NoSuchJobException;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.util.Assert;
+
+/**
+ * A {@link PropertyEditor} that delegates to a {@link JobLocator}.
+ * @author Dave Syer
+ *
+ */
+public class JobPropertyEditor extends PropertyEditorSupport implements InitializingBean {
+
+ private JobLocator jobLocator;
+
+ /* (non-Javadoc)
+ * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
+ */
+ public void afterPropertiesSet() throws Exception {
+ Assert.notNull(jobLocator, "JobLocator is required");
+ }
+
+ /**
+ * Accept job name and convert to {@link Job} through the injected {@link JobLocator}.
+ *
+ * @see java.beans.PropertyEditorSupport#setAsText(java.lang.String)
+ */
+ public void setAsText(String text) throws IllegalArgumentException {
+ try {
+ setValue(jobLocator.getJob(text));
+ }
+ catch (NoSuchJobException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ /**
+ * Extract the name from the {@link JobIdentifier}.
+ *
+ * @see java.beans.PropertyEditorSupport#getAsText()
+ */
+ public String getAsText() {
+ Job job = (Job) getValue();
+ if (job == null) {
+ return null;
+ }
+ return job.getName();
+ }
+
+ /**
+ * Public setter for the {@link JobLocator}.
+ * @param jobLocator the jobLocator to set
+ */
+ public void setJobLocator(JobLocator jobLocator) {
+ this.jobLocator = jobLocator;
+ }
+}
\ No newline at end of file
diff --git a/spring-batch-execution/src/main/java/org/springframework/batch/execution/bootstrap/support/SimpleExportedJobLauncher.java b/spring-batch-execution/src/main/java/org/springframework/batch/execution/bootstrap/support/SimpleExportedJobLauncher.java
new file mode 100644
index 000000000..675b21ebf
--- /dev/null
+++ b/spring-batch-execution/src/main/java/org/springframework/batch/execution/bootstrap/support/SimpleExportedJobLauncher.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright 2006-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.batch.execution.bootstrap.support;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Properties;
+
+import org.springframework.batch.core.domain.Job;
+import org.springframework.batch.core.domain.JobExecution;
+import org.springframework.batch.core.domain.JobLocator;
+import org.springframework.batch.core.domain.JobParameters;
+import org.springframework.batch.core.domain.NoSuchJobException;
+import org.springframework.batch.core.domain.StepExecution;
+import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException;
+import org.springframework.batch.core.runtime.JobParametersFactory;
+import org.springframework.batch.execution.launch.JobLauncher;
+import org.springframework.batch.support.PropertiesConverter;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.util.Assert;
+
+/**
+ * @author Dave Syer
+ *
+ */
+public class SimpleExportedJobLauncher implements ExportedJobLauncher, InitializingBean {
+
+ private JobLauncher launcher;
+
+ private JobLocator jobLocator;
+
+ private Map registry = new HashMap();
+
+ private JobParametersFactory jobParametersFactory = new DefaultJobParametersFactory();
+
+ /* (non-Javadoc)
+ * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
+ */
+ public void afterPropertiesSet() throws Exception {
+ Assert.notNull(launcher, "JobLauncher must be provided.");
+ Assert.notNull(jobLocator, "JobLocator must be provided.");
+ }
+
+ /**
+ * Public setter for the {@link JobLauncher}.
+ * @param launcher the launcher to set
+ */
+ public void setLauncher(JobLauncher launcher) {
+ this.launcher = launcher;
+ }
+
+ /**
+ * Public setter for the JobLocator.
+ * @param jobLocator the jobLocator to set
+ */
+ public void setJobLocator(JobLocator jobLocator) {
+ this.jobLocator = jobLocator;
+ }
+
+ /**
+ * Public setter for the JobParametersFactory.
+ * @param jobParametersFactory the jobParametersFactory to set
+ */
+ public void setJobParametersFactory(JobParametersFactory jobParametersFactory) {
+ this.jobParametersFactory = jobParametersFactory;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.batch.execution.bootstrap.support.ExportedJobLauncher#getStatistics()
+ */
+ public Properties getStatistics() {
+ Properties result = new Properties();
+ int i = 0;
+ for (Iterator iterator = registry.keySet().iterator(); iterator.hasNext();) {
+ String key = (String) iterator.next();
+ JobExecution execution = (JobExecution) registry.get(key);
+ addStatistics(result, execution, "job" + i + ".");
+ i++;
+ }
+ return result;
+ }
+
+ /**
+ * @param result
+ * @param execution
+ */
+ private void addStatistics(Properties result, JobExecution execution, String prefix) {
+ int i = 0;
+ for (Iterator iterator = execution.getStepExecutions().iterator(); iterator.hasNext();) {
+ StepExecution stepExecution = (StepExecution) iterator.next();
+ Properties statistics = stepExecution.getStatistics();
+ for (Iterator iter = statistics.keySet().iterator(); iter.hasNext();) {
+ String key = (String) iter.next();
+ result.setProperty(prefix + "step" + i + "." + key, statistics.getProperty(key));
+ }
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.batch.execution.bootstrap.support.ExportedJobLauncher#isRunning()
+ */
+ public boolean isRunning() {
+ for (Iterator iterator = registry.keySet().iterator(); iterator.hasNext();) {
+ String key = (String) iterator.next();
+ JobExecution execution = (JobExecution) registry.get(key);
+ if (execution.isRunning()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.batch.execution.bootstrap.support.ExportedJobLauncher#run(java.lang.String)
+ */
+ public String run(String name) {
+ return run(name, null);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.batch.execution.bootstrap.support.ExportedJobLauncher#run(java.lang.String,
+ * java.lang.String)
+ */
+ public String run(String name, String params) {
+
+ Job job;
+ try {
+ job = jobLocator.getJob(name);
+ }
+ catch (NoSuchJobException e) {
+ return e.getClass().getName() + ": " + e.getMessage();
+ }
+
+ JobParameters jobParameters = new JobParameters();
+ if (params != null) {
+ jobParameters = jobParametersFactory.getJobParameters(PropertiesConverter.stringToProperties(params));
+ }
+
+ JobExecution execution;
+ try {
+ execution = launcher.run(job, jobParameters);
+ }
+ catch (JobExecutionAlreadyRunningException e) {
+ return e.getClass().getName() + ": " + e.getMessage();
+ }
+
+ registry.put(name + params, execution);
+
+ return execution.toString();
+
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.batch.execution.bootstrap.support.ExportedJobLauncher#stop()
+ */
+ public void stop() {
+ for (Iterator iterator = registry.keySet().iterator(); iterator.hasNext();) {
+ String key = (String) iterator.next();
+ JobExecution execution = (JobExecution) registry.get(key);
+ execution.stop();
+ }
+ registry.clear();
+ }
+
+}
diff --git a/spring-batch-execution/src/test/java/org/springframework/batch/execution/bootstrap/support/JobPropertyEditorTests.java b/spring-batch-execution/src/test/java/org/springframework/batch/execution/bootstrap/support/JobPropertyEditorTests.java
new file mode 100644
index 000000000..d62f826ad
--- /dev/null
+++ b/spring-batch-execution/src/test/java/org/springframework/batch/execution/bootstrap/support/JobPropertyEditorTests.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2006-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.batch.execution.bootstrap.support;
+
+import junit.framework.TestCase;
+
+import org.springframework.batch.core.domain.Job;
+import org.springframework.batch.core.domain.JobLocator;
+import org.springframework.batch.core.domain.NoSuchJobException;
+
+/**
+ * @author Dave Syer
+ *
+ */
+public class JobPropertyEditorTests extends TestCase {
+
+ private JobPropertyEditor editor = new JobPropertyEditor();
+ private Job job = new Job();
+
+ /* (non-Javadoc)
+ * @see junit.framework.TestCase#setUp()
+ */
+ protected void setUp() throws Exception {
+ super.setUp();
+ editor.setJobLocator(new JobLocator() {
+ public Job getJob(String name) throws NoSuchJobException {
+ job.setName(name);
+ return job;
+ }
+ });
+ }
+
+ public void testMandatoryProperties() throws Exception {
+ editor = new JobPropertyEditor();
+ try {
+ editor.afterPropertiesSet();
+ fail("Expected IllegalArgumentException");
+ }
+ catch (IllegalArgumentException e) {
+ // expected
+ }
+ }
+
+ /**
+ * Test method for
+ * {@link org.springframework.batch.execution.bootstrap.support.JobParametersPropertyEditor#setAsText(java.lang.String)}.
+ */
+ public void testSetAsTextString() {
+ editor.setAsText("foo");
+ Job job = (Job) editor.getValue();
+ assertEquals(job, job);
+ }
+
+ /**
+ * Test method for
+ * {@link org.springframework.batch.execution.bootstrap.support.JobParametersPropertyEditor#getAsText()}.
+ */
+ public void testGetAsText() {
+ editor.setAsText("foo");
+ assertEquals("foo", editor.getAsText());
+ }
+
+}
diff --git a/spring-batch-samples/.settings/jmxLauncher.launch b/spring-batch-samples/.settings/jmxLauncher.launch
index 2d54a277c..d3b19648a 100644
--- a/spring-batch-samples/.settings/jmxLauncher.launch
+++ b/spring-batch-samples/.settings/jmxLauncher.launch
@@ -8,6 +8,8 @@
+
+
diff --git a/spring-batch-samples/src/main/resources/jobs/adhocLoopJob.xml b/spring-batch-samples/src/main/resources/jobs/adhocLoopJob.xml
index 62d21e823..13dd2baaa 100644
--- a/spring-batch-samples/src/main/resources/jobs/adhocLoopJob.xml
+++ b/spring-batch-samples/src/main/resources/jobs/adhocLoopJob.xml
@@ -1,153 +1,155 @@
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+