RESOLVED: BATCH-119: ExitStatus can't be used in JMX (or other remote interfaces).
http://opensource.atlassian.com/projects/spring/browse/BATCH-119
This commit is contained in:
@@ -73,7 +73,7 @@ public abstract class AbstractJobLauncher implements JobLauncher,
|
||||
* @param jobRuntimeInformationFactory
|
||||
* the jobRuntimeInformationFactory to set
|
||||
*/
|
||||
public void setJobRuntimeInformationFactory(
|
||||
public void setJobIdentifierFactory(
|
||||
JobIdentifierFactory jobRuntimeInformationFactory) {
|
||||
this.jobRuntimeInformationFactory = jobRuntimeInformationFactory;
|
||||
}
|
||||
@@ -103,14 +103,14 @@ public abstract class AbstractJobLauncher implements JobLauncher,
|
||||
*
|
||||
* @param batchContainer
|
||||
*/
|
||||
public void setBatchContainer(JobExecutorFacade batchContainer) {
|
||||
public void setJobExecutorFacade(JobExecutorFacade batchContainer) {
|
||||
this.batchContainer = batchContainer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that mandatory properties are set.
|
||||
*
|
||||
* @see #setBatchContainer(JobExecutorFacade)
|
||||
* @see #setJobExecutorFacade(JobExecutorFacade)
|
||||
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
|
||||
*/
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
@@ -203,7 +203,7 @@ public abstract class AbstractJobLauncher implements JobLauncher,
|
||||
* @throws NoSuchJobConfigurationException
|
||||
* if the job configuration cannot be located
|
||||
*
|
||||
* @see #setJobRuntimeInformationFactory(JobIdentifierFactory)
|
||||
* @see #setJobIdentifierFactory(JobIdentifierFactory)
|
||||
* @see org.springframework.context.Lifecycle#start()
|
||||
*/
|
||||
public ExitStatus run() throws NoSuchJobConfigurationException {
|
||||
|
||||
@@ -58,7 +58,7 @@ public class SimpleJobLauncher implements JobLauncher {
|
||||
/**
|
||||
* Check that mandatory properties are set.
|
||||
*
|
||||
* @see #setBatchContainer(JobExecutorFacade)
|
||||
* @see #setJobExecutorFacade(JobExecutorFacade)
|
||||
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
|
||||
*/
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
@@ -124,7 +124,7 @@ public class SimpleJobLauncher implements JobLauncher {
|
||||
* default name is taken from the enclosed {@link JobConfiguration}.
|
||||
* @throws NoSuchJobConfigurationException if the job configuration cannot be located
|
||||
*
|
||||
* @see #setJobRuntimeInformationFactory(JobIdentifierFactory)
|
||||
* @see #setJobIdentifierFactory(JobIdentifierFactory)
|
||||
* @see org.springframework.context.Lifecycle#start()
|
||||
*/
|
||||
public ExitStatus run(){
|
||||
|
||||
@@ -53,10 +53,12 @@ import org.springframework.util.Assert;
|
||||
* @author Dave Syer
|
||||
* @since 2.1
|
||||
*/
|
||||
public class TaskExecutorJobLauncher extends AbstractJobLauncher implements ApplicationListener,
|
||||
NotificationPublisherAware, ApplicationEventPublisherAware {
|
||||
public class TaskExecutorJobLauncher extends AbstractJobLauncher implements
|
||||
ApplicationListener, NotificationPublisherAware,
|
||||
ApplicationEventPublisherAware {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(TaskExecutorJobLauncher.class);
|
||||
private static final Log logger = LogFactory
|
||||
.getLog(TaskExecutorJobLauncher.class);
|
||||
|
||||
private TaskExecutor taskExecutor = new SyncTaskExecutor();
|
||||
|
||||
@@ -68,9 +70,11 @@ public class TaskExecutorJobLauncher extends AbstractJobLauncher implements Appl
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.context.ApplicationEventPublisherAware#setApplicationEventPublisher(org.springframework.context.ApplicationEventPublisher)
|
||||
*/
|
||||
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
|
||||
public void setApplicationEventPublisher(
|
||||
ApplicationEventPublisher applicationEventPublisher) {
|
||||
this.applicationEventPublisher = applicationEventPublisher;
|
||||
}
|
||||
|
||||
@@ -78,7 +82,8 @@ public class TaskExecutorJobLauncher extends AbstractJobLauncher implements Appl
|
||||
* Setter for the {@link TaskExecutor}. Defaults to a
|
||||
* {@link SyncTaskExecutor}.
|
||||
*
|
||||
* @param taskExecutor the taskExecutor to set
|
||||
* @param taskExecutor
|
||||
* the taskExecutor to set
|
||||
*/
|
||||
public void setTaskExecutor(TaskExecutor taskExecutor) {
|
||||
this.taskExecutor = taskExecutor;
|
||||
@@ -86,16 +91,19 @@ public class TaskExecutorJobLauncher extends AbstractJobLauncher implements Appl
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.jmx.export.notification.NotificationPublisherAware#setNotificationPublisher(org.springframework.jmx.export.notification.NotificationPublisher)
|
||||
*/
|
||||
public void setNotificationPublisher(NotificationPublisher notificationPublisher) {
|
||||
public void setNotificationPublisher(
|
||||
NotificationPublisher notificationPublisher) {
|
||||
this.notificationPublisher = notificationPublisher;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the provided container using the task executor provided.
|
||||
*
|
||||
* @throws IllegalStateException if JobConfiguration is null.
|
||||
* @throws IllegalStateException
|
||||
* if JobConfiguration is null.
|
||||
*/
|
||||
protected ExitStatus doRun(final JobIdentifier runtimeInformation) {
|
||||
|
||||
@@ -105,14 +113,16 @@ public class TaskExecutorJobLauncher extends AbstractJobLauncher implements Appl
|
||||
public void run() {
|
||||
try {
|
||||
batchContainer.start(runtimeInformation);
|
||||
}
|
||||
catch (NoSuchJobConfigurationException e) {
|
||||
applicationEventPublisher.publishEvent(new RepeatOperationsApplicationEvent(runtimeInformation,
|
||||
"No such job", RepeatOperationsApplicationEvent.ERROR));
|
||||
logger.error("JobConfiguration could not be located inside Runnable for runtime information: ["
|
||||
+ runtimeInformation + "]", e);
|
||||
}
|
||||
finally {
|
||||
} catch (NoSuchJobConfigurationException e) {
|
||||
applicationEventPublisher
|
||||
.publishEvent(new RepeatOperationsApplicationEvent(
|
||||
runtimeInformation, "No such job",
|
||||
RepeatOperationsApplicationEvent.ERROR));
|
||||
logger
|
||||
.error(
|
||||
"JobConfiguration could not be located inside Runnable for runtime information: ["
|
||||
+ runtimeInformation + "]", e);
|
||||
} finally {
|
||||
unregister(runtimeInformation);
|
||||
}
|
||||
}
|
||||
@@ -125,11 +135,13 @@ public class TaskExecutorJobLauncher extends AbstractJobLauncher implements Appl
|
||||
/**
|
||||
* Delegates to the underlying {@link JobExecutorFacade}. Does not wait for
|
||||
* the jobs to stop (probably therefore returns immediately).
|
||||
* @throws NoSuchJobExecutionException
|
||||
*
|
||||
* @throws NoSuchJobExecutionException
|
||||
*
|
||||
* @see org.springframework.context.Lifecycle#stop()
|
||||
*/
|
||||
protected void doStop(JobIdentifier runtimeInformation) throws NoSuchJobExecutionException {
|
||||
protected void doStop(JobIdentifier runtimeInformation)
|
||||
throws NoSuchJobExecutionException {
|
||||
batchContainer.stop(runtimeInformation);
|
||||
// TODO: wait for the jobs to stop?
|
||||
}
|
||||
@@ -146,9 +158,11 @@ public class TaskExecutorJobLauncher extends AbstractJobLauncher implements Appl
|
||||
if (applicationEvent instanceof RepeatOperationsApplicationEvent) {
|
||||
RepeatOperationsApplicationEvent event = (RepeatOperationsApplicationEvent) applicationEvent;
|
||||
int type = event.getType();
|
||||
if (type == RepeatOperationsApplicationEvent.OPEN || type == RepeatOperationsApplicationEvent.CLOSE
|
||||
if (type == RepeatOperationsApplicationEvent.OPEN
|
||||
|| type == RepeatOperationsApplicationEvent.CLOSE
|
||||
|| type == RepeatOperationsApplicationEvent.ERROR) {
|
||||
String message = event.getMessage() + "; source=" + event.getSource();
|
||||
String message = event.getMessage() + "; source="
|
||||
+ event.getSource();
|
||||
logger.info(message);
|
||||
publish(message);
|
||||
}
|
||||
@@ -164,8 +178,8 @@ public class TaskExecutorJobLauncher extends AbstractJobLauncher implements Appl
|
||||
* for information purposes only.
|
||||
*
|
||||
* @return Properties representing the last {@link JobExecutionContext}
|
||||
* objects passed up from the underlying execution. If there are no jobs
|
||||
* running it will be empty.
|
||||
* objects passed up from the underlying execution. If there are no
|
||||
* jobs running it will be empty.
|
||||
*/
|
||||
public Properties getStatistics() {
|
||||
if (batchContainer instanceof StatisticsProvider) {
|
||||
@@ -180,8 +194,17 @@ public class TaskExecutorJobLauncher extends AbstractJobLauncher implements Appl
|
||||
*/
|
||||
private void publish(String message) {
|
||||
if (notificationPublisher != null) {
|
||||
notificationPublisher.sendNotification(new Notification("RepeatOperationsApplicationEvent", this,
|
||||
notificationCount++, message));
|
||||
Notification notification = new Notification(
|
||||
"RepeatOperationsApplicationEvent", 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
package org.springframework.batch.execution.bootstrap.support;
|
||||
|
||||
import org.springframework.batch.execution.bootstrap.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.
|
||||
*
|
||||
* @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 configuration 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}.
|
||||
*
|
||||
* @see #run()
|
||||
*/
|
||||
String run(String name);
|
||||
|
||||
/**
|
||||
* Stop all running jobs.
|
||||
*
|
||||
* @see JobLauncher#stop()
|
||||
*/
|
||||
void stop();
|
||||
|
||||
/**
|
||||
* Enquire if any jobs are still running.
|
||||
*
|
||||
* @return true if any jobs are running.
|
||||
*
|
||||
* @see JobLauncher#isRunning()
|
||||
*/
|
||||
boolean isRunning();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package org.springframework.batch.execution.bootstrap.support;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.springframework.beans.SimpleTypeConverter;
|
||||
import org.springframework.beans.TypeConverter;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* A {@link MethodInterceptor} that can mask a mismatch between the target and
|
||||
* proxy interfaces by converting the returned value to the correct type.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class TypeConverterMethodInterceptor implements MethodInterceptor {
|
||||
|
||||
// Get the default PropertyEditorRegistry free. TODO: make this
|
||||
// configurable.
|
||||
private TypeConverter typeConverter = new SimpleTypeConverter();
|
||||
|
||||
/**
|
||||
* Public setter for the {@link TypeConverter} property. Defaults to a
|
||||
* {@link SimpleTypeConverter}.
|
||||
*
|
||||
* @param typeConverter
|
||||
* the typeConverter to set
|
||||
*/
|
||||
public void setTypeConverter(TypeConverter typeConverter) {
|
||||
this.typeConverter = typeConverter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke the method with the same name and arguments on the target, but
|
||||
* possibly with a different return type. If the return type doesn't match
|
||||
* attempt to convert it.
|
||||
*
|
||||
* @see org.aopalliance.intercept.MethodInterceptor#invoke(org.aopalliance.intercept.MethodInvocation)
|
||||
*/
|
||||
public Object invoke(MethodInvocation invocation) throws Throwable {
|
||||
|
||||
// The method called on the proxy
|
||||
Method invoked = invocation.getMethod();
|
||||
|
||||
// The corresponding method on the target if there is one...
|
||||
Method method = ReflectionUtils.findMethod(invocation.getThis()
|
||||
.getClass(), invoked.getName(), invoked.getParameterTypes());
|
||||
|
||||
// If there was no such method do nothing... TODO: throw Exception?
|
||||
if (method == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Invoke the target method
|
||||
Object result = ReflectionUtils.invokeMethod(method, invocation
|
||||
.getThis(), invocation.getArguments());
|
||||
if (result == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// If the return type doesn't match, try and convert it
|
||||
if (!ClassUtils.isAssignableValue(invoked.getReturnType(), result)) {
|
||||
result = convert(result, invoked.getReturnType());
|
||||
}
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
private Object convert(Object result, Class returnType) {
|
||||
if (returnType.isAssignableFrom(String.class)) {
|
||||
return result.toString();
|
||||
}
|
||||
return typeConverter.convertIfNecessary(result, returnType);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<html>
|
||||
<body>
|
||||
<p>
|
||||
Support classes for use in bootstrap implementations or configurations.
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user