OPEN - issue BATCH-295: JobLauncher should have only one run method.

http://jira.springframework.org/browse/BATCH-295

Add a property editor for JobIdentifier so the JMX demo can work.
This commit is contained in:
dsyer
2008-01-21 11:37:12 +00:00
parent c4e47ffab0
commit badcf74c7b
11 changed files with 410 additions and 145 deletions

View File

@@ -18,38 +18,29 @@ package org.springframework.batch.execution.bootstrap.support;
import java.util.Properties;
import org.springframework.batch.core.domain.JobExecution;
import org.springframework.batch.core.domain.JobIdentifier;
import org.springframework.batch.execution.launch.JobLauncher;
import org.springframework.batch.repeat.ExitStatus;
/**
* Interface to expose for remote management of jobs. Similar to
* {@link JobLauncher}, but replaces {@link ExitStatus} with String in return
* types, so it can be inspected by remote clients like the jconsole from the
* JRE without any links to Spring Batch.
* {@link JobLauncher}, but replaces {@link JobExecution} and
* {@link JobIdentifier} with Strings in return types and method parameters, so
* it can be inspected by remote clients like the jconsole from the JRE without
* any links to Spring Batch.
*
* @author Dave Syer
*
*/
public interface ExportedJobLauncher {
/**
* Launch a job and get back a representation of the {@link ExitStatus}
* returned by a {@link JobLauncher}. Normally the launch will be
* asynchronous, so the possible values of the return type are constrained
* (it will never be {@link ExitStatus#CONTINUABLE}).
*
* @return a representation of the {@link ExitStatus} returned by a
* {@link JobLauncher}.
*/
String run();
/**
* Launch a job with the given name.
*
* @param name the name of the job to launch
* @return a representation of the {@link ExitStatus} returned by a
* {@link JobLauncher}.
*
* @return a representation of the {@link JobExecution} returned by a
* {@link JobLauncher}.
*
* @see #run()
*/
String run(String name);
@@ -69,11 +60,12 @@ public interface ExportedJobLauncher {
* @see JobLauncher#isRunning()
*/
boolean isRunning();
/**
* Query statistics of currently executing jobs.
*
* @return properties representing last known state of currently executing jobs
* @return properties representing last known state of currently executing
* jobs
*/
public Properties getStatistics();

View File

@@ -0,0 +1,65 @@
/*
* 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.PropertyEditorSupport;
import org.springframework.batch.core.domain.Job;
import org.springframework.batch.core.domain.JobIdentifier;
import org.springframework.batch.core.runtime.JobIdentifierFactory;
import org.springframework.batch.core.runtime.SimpleJobIdentifierFactory;
/**
* Simple adapter for a {@link JobIdentifierFactory} that can be used to convert
* from a {@link Job} name to a {@link JobIdentifier}.
*
* @author Dave Syer
*
*/
public class JobIdentifierPropertyEditor extends PropertyEditorSupport {
private JobIdentifierFactory jobIdentifierFactory = new SimpleJobIdentifierFactory();
/**
* Public setter for the {@link JobIdentifierFactory}.
* @param jobIdentifierFactory the jobIdentifierFactory to set
*/
public void setJobIdentifierFactory(JobIdentifierFactory jobIdentifierFactory) {
this.jobIdentifierFactory = jobIdentifierFactory;
}
/**
* Accept name of {@link Job} and create a {@link JobIdentifier}.
*
* @see java.beans.PropertyEditorSupport#setAsText(java.lang.String)
*/
public void setAsText(String text) throws IllegalArgumentException {
setValue(jobIdentifierFactory.getJobIdentifier(text));
}
/**
* Extract the name from the {@link JobIdentifier}.
*
* @see java.beans.PropertyEditorSupport#getAsText()
*/
public String getAsText() {
JobIdentifier identifier = (JobIdentifier) getValue();
if (identifier == null) {
return null;
}
return identifier.getName();
}
}

View File

@@ -24,9 +24,12 @@ import java.util.List;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.batch.support.DefaultPropertyEditorRegistrar;
import org.springframework.beans.PropertyEditorRegistry;
import org.springframework.beans.SimpleTypeConverter;
import org.springframework.beans.TypeConverter;
import org.springframework.beans.TypeMismatchException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
@@ -37,13 +40,32 @@ import org.springframework.util.ReflectionUtils;
* @author Dave Syer
*
*/
public class TypeConverterMethodInterceptor implements MethodInterceptor {
public class TypeConverterMethodInterceptor extends DefaultPropertyEditorRegistrar implements MethodInterceptor,
InitializingBean {
// Get the default PropertyEditorRegistry free.
private TypeConverter typeConverter = new SimpleTypeConverter();
private TypeConverter typeConverter;
private boolean convertException = false;
/**
* Ensure that the {@link TypeConverter} is set up, creating one if
* necessary, and if it is a {@link PropertyEditorRegistry}, register the
* custom editors. (If it is not a {@link PropertyEditorRegistry} then the
* custom editors are ignored).
*
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
*/
public void afterPropertiesSet() throws Exception {
if (typeConverter == null) {
SimpleTypeConverter converter = new SimpleTypeConverter();
typeConverter = converter;
}
if (typeConverter instanceof PropertyEditorRegistry) {
registerCustomEditors((PropertyEditorRegistry) typeConverter);
}
}
/**
* Set a flag that will cause exceptions during method invocation to be
* caught and treated as a result. This can be useful if used over JConsole,
@@ -107,7 +129,7 @@ public class TypeConverterMethodInterceptor implements MethodInterceptor {
}
});
if (methods.size()==1) {
if (methods.size() == 1) {
method = (Method) methods.get(0);
for (int i = 0; i < arguments.length; i++) {
Object arg = arguments[i];

View File

@@ -18,7 +18,6 @@ package org.springframework.batch.execution.repository.dao;
import java.util.List;
import org.springframework.batch.core.domain.Job;
import org.springframework.batch.core.domain.JobExecution;
import org.springframework.batch.core.domain.JobIdentifier;
import org.springframework.batch.core.domain.JobInstance;

View File

@@ -0,0 +1,62 @@
/*
* 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 org.springframework.batch.core.domain.JobIdentifier;
import org.springframework.batch.core.runtime.SimpleJobIdentifierFactory;
import junit.framework.TestCase;
/**
* @author Dave Syer
*
*/
public class JobIdentifierPropertyEditorTests extends TestCase {
private JobIdentifierPropertyEditor editor = new JobIdentifierPropertyEditor();
/**
* Test method for {@link org.springframework.batch.execution.bootstrap.support.JobIdentifierPropertyEditor#setJobIdentifierFactory(org.springframework.batch.core.runtime.JobIdentifierFactory)}.
*/
public void testSetJobIdentifierFactory() {
editor.setJobIdentifierFactory(new SimpleJobIdentifierFactory() {
public JobIdentifier getJobIdentifier(String name) {
return super.getJobIdentifier("test:"+name);
}
});
editor.setAsText("foo");
JobIdentifier identifier = (JobIdentifier) editor.getValue();
assertEquals("test:foo", identifier.getName());
}
/**
* Test method for {@link org.springframework.batch.execution.bootstrap.support.JobIdentifierPropertyEditor#setAsText(java.lang.String)}.
*/
public void testSetAsTextString() {
editor.setAsText("foo");
JobIdentifier identifier = (JobIdentifier) editor.getValue();
assertEquals("foo", identifier.getName());
}
/**
* Test method for {@link org.springframework.batch.execution.bootstrap.support.JobIdentifierPropertyEditor#getAsText()}.
*/
public void testGetAsText() {
editor.setAsText("foo");
assertEquals("foo", editor.getAsText());
}
}

View File

@@ -17,6 +17,15 @@ public class TypeConverterMethodInterceptorTests extends TestCase {
private List list = new ArrayList();
/*
* (non-Javadoc)
* @see junit.framework.TestCase#setUp()
*/
protected void setUp() throws Exception {
super.setUp();
interceptor.afterPropertiesSet();
}
/**
* Even though TestBean does not implement Test, the proxy will invoke a
* method is called with the same signature.
@@ -72,6 +81,36 @@ public class TypeConverterMethodInterceptorTests extends TestCase {
assertEquals("FOO:true", proxy.getBean());
}
public void testInvokeWithNoMethodMatch() throws Exception {
ProxyFactory factory = new ProxyFactory(Test.class, interceptor);
factory.setTarget(this);
Test proxy = (Test) factory.getProxy();
assertNull(proxy.getBean());
}
public void testInvokeWithError() throws Exception {
ProxyFactory factory = new ProxyFactory(Test.class, interceptor);
factory.setTarget(new TestBean(true));
Test proxy = (Test) factory.getProxy();
try {
assertNull(proxy.error());
fail("Expected RuntimeException");
}
catch (RuntimeException e) {
// expected
assertEquals("Foo", e.getMessage());
}
}
public void testInvokeWithErrorAndConvert() throws Exception {
ProxyFactory factory = new ProxyFactory(Test.class, interceptor);
factory.setTarget(new TestBean(true));
Test proxy = (Test) factory.getProxy();
interceptor.setConvertException(true);
String msg = proxy.error();
assertTrue("Message is not a stacktrace: "+msg, msg.indexOf("RuntimeException: Foo")>=0);
}
public void testInvokeWithMethodParameterConversionToBoolean() throws Exception {
ProxyFactory factory = new ProxyFactory(Test.class, interceptor);
factory.setTarget(new TestBean(true));
@@ -94,11 +133,12 @@ public class TypeConverterMethodInterceptorTests extends TestCase {
try {
proxy.getInvalid();
fail("Expected TypeMismatchException");
} catch (TypeMismatchException e) {
}
catch (TypeMismatchException e) {
// expected
}
}
public void testTypeConverter() throws Exception {
final TestCase testCase = this;
interceptor.setTypeConverter(new SimpleTypeConverter() {
@@ -112,6 +152,12 @@ public class TypeConverterMethodInterceptorTests extends TestCase {
assertEquals(testCase, proxy.getInvalid());
}
public void testTypeConverterAfterPropertiesSet() throws Exception {
testTypeConverter();
interceptor.afterPropertiesSet();
testTypeConverter();
}
public void testInvokeWithMethodParameterConversionWithPropertyEditor() throws Exception {
final TestBean bean = new TestBean(false);
SimpleTypeConverter converter = new SimpleTypeConverter();
@@ -135,17 +181,19 @@ public class TypeConverterMethodInterceptorTests extends TestCase {
String relayPattern(String pattern);
TestCase getInvalid();
int getValue();
void operate();
String grab(String value);
String relayBean(String value);
String error();
}
// N.B. TestBean intentionally does not implement Test!
// N.B. TestBean intentionally does not implement Test!
public class TestBean {
private boolean test;
@@ -173,7 +221,7 @@ public class TypeConverterMethodInterceptorTests extends TestCase {
public TestBean getInvalid() {
return this;
}
public String getValue() {
return "123";
}
@@ -181,14 +229,18 @@ public class TypeConverterMethodInterceptorTests extends TestCase {
public void operate() {
list.add("FOO");
}
public String grab(boolean flag) {
return "flag:"+flag;
return "flag:" + flag;
}
public String toString() {
return "FOO:" + test;
}
public String error() {
throw new RuntimeException("Foo");
}
}
}