Skip to content

validate step listeners #3708

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -214,9 +214,11 @@ public static boolean isListener(Object target, Class<?> listenerType, ListenerM
if (target == null) {
return false;
}

if (listenerType.isInstance(target)) {
return true;
}

if (target instanceof Advised) {
TargetSource targetSource = ((Advised) target).getTargetSource();
if (targetSource != null && targetSource.getTargetClass() != null
Expand All @@ -228,11 +230,21 @@ public static boolean isListener(Object target, Class<?> listenerType, ListenerM
logger.warn(String.format("%s is an interface. The implementing class will not be queried for annotation based listener configurations. If using @StepScope on a @Bean method, be sure to return the implementing class so listener annotations can be used.", targetSource.getTargetClass().getName()));
}
}

for (ListenerMetaData metaData : metaDataValues) {
if (MethodInvokerUtils.getMethodInvokerByAnnotation(metaData.getAnnotation(), target) != null) {
Class<?> listenerInterface = metaData.getListenerInterface();
if (listenerInterface.isInstance(target)) {
return true;
}

if (metaData.getAnnotation() != null) {
MethodInvoker annotatedMethod = getMethodInvokerByAnnotation(metaData.getAnnotation(), target);
if (annotatedMethod != null) {
return true;
}
}
}

return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,14 @@
*/
public interface ListenerMetaData {

public String getMethodName();
String getMethodName();

public Class<? extends Annotation> getAnnotation();
Class<? extends Annotation> getAnnotation();

public Class<?> getListenerInterface();
Class<?> getListenerInterface();

public String getPropertyName();
String getPropertyName();

public Class<?>[] getParamTypes();
Class<?>[] getParamTypes();

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,34 @@
*/
package org.springframework.batch.core.listener;

import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.springframework.batch.core.StepListener;
import org.springframework.batch.core.jsr.JsrStepListenerMetaData;

/**
* This {@link AbstractListenerFactoryBean} implementation is used to create a
* {@link StepListener}.
*
* @author Lucas Ward
* @author Dan Garrette
* @author Alexei KLENIN
* @since 2.0
* @see AbstractListenerFactoryBean
* @see StepListenerMetaData
*/
public class StepListenerFactoryBean extends AbstractListenerFactoryBean<StepListener> {
private static final String STR_STEP_LISTENER_ANNOTATIONS_LIST = Stream
.of(StepListenerMetaData.values())
.map(StepListenerMetaData::getAnnotation)
.map(Class::getSimpleName)
.map(annotation -> String.format("\t- @%s\n", annotation))
.collect(Collectors.joining());
private static final String ERR_OBJECT_NOT_STEP_LISTENER_TEMPLATE =
"Object of type [%s] is not a valid step listener. " +
"It must ether implement StepListener interface or have methods annotated with any of:\n" +
STR_STEP_LISTENER_ANNOTATIONS_LIST;

@Override
protected ListenerMetaData getMetaDataFromPropertyName(String propertyName) {
Expand All @@ -49,6 +64,11 @@ public Class<StepListener> getObjectType() {
return StepListener.class;
}

@Override
public void setDelegate(Object delegate) {
super.setDelegate(assertListener(delegate));
}

/**
* Convenience method to wrap any object and expose the appropriate
* {@link StepListener} interfaces.
Expand All @@ -72,6 +92,30 @@ public static StepListener getListener(Object delegate) {
* annotations
*/
public static boolean isListener(Object delegate) {
return AbstractListenerFactoryBean.isListener(delegate, StepListener.class, StepListenerMetaData.values());
ListenerMetaData[] metaDataValues = Stream
.<ListenerMetaData> concat(
Stream.of(StepListenerMetaData.values()),
Stream.of(JsrStepListenerMetaData.values()))
.toArray(ListenerMetaData[]::new);

return AbstractListenerFactoryBean.isListener(delegate, StepListener.class, metaDataValues);
}

/**
* Asserts that {@code delegate} is a valid step listener. If this is not a case, throws an
* {@link IllegalArgumentException} with message detailing the problem. Object is a valid
* step listener is it ether implements interface {@link StepListener} or has any method
* annotated with one of marker annotations.
*
* @param delegate the object to check
* @return valid step execution listener
*/
public static Object assertListener(Object delegate) {
if (!isListener(delegate)) {
throw new IllegalArgumentException(String.format(
ERR_OBJECT_NOT_STEP_LISTENER_TEMPLATE, delegate.getClass().getName()));
}

return delegate;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import org.springframework.batch.core.ChunkListener;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.StepExecutionListener;
import org.springframework.batch.core.StepListener;
import org.springframework.batch.core.annotation.AfterChunk;
import org.springframework.batch.core.annotation.AfterChunkError;
import org.springframework.batch.core.annotation.BeforeChunk;
Expand All @@ -47,6 +48,7 @@
* @author Dave Syer
* @author Michael Minella
* @author Mahmoud Ben Hassine
* @author Alexei Klenin
*
* @since 2.2
*
Expand Down Expand Up @@ -159,10 +161,9 @@ public B listener(Object listener) {
chunkListenerMethods.addAll(ReflectionUtils.findMethod(listener.getClass(), AfterChunk.class));
chunkListenerMethods.addAll(ReflectionUtils.findMethod(listener.getClass(), AfterChunkError.class));

if(chunkListenerMethods.size() > 0) {
StepListenerFactoryBean factory = new StepListenerFactoryBean();
factory.setDelegate(listener);
this.listener((ChunkListener) factory.getObject());
if (!chunkListenerMethods.isEmpty()) {
StepListener stepListener = StepListenerFactoryBean.getListener(listener);
this.listener((ChunkListener) stepListener);
}

@SuppressWarnings("unchecked")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@
* @author Chris Schaefer
* @author Michael Minella
* @author Mahmoud Ben Hassine
* @author Alexei Klenin
*
* @since 2.2
*/
Expand Down Expand Up @@ -204,18 +205,14 @@ public SimpleStepBuilder<I, O> listener(Object listener) {
skipListenerMethods.addAll(ReflectionUtils.findMethod(listener.getClass(), OnSkipInProcess.class));
skipListenerMethods.addAll(ReflectionUtils.findMethod(listener.getClass(), OnSkipInWrite.class));

if(skipListenerMethods.size() > 0) {
StepListenerFactoryBean factory = new StepListenerFactoryBean();
factory.setDelegate(listener);
skipListeners.add((SkipListener) factory.getObject());
if (!skipListenerMethods.isEmpty()) {
StepListener stepListener = StepListenerFactoryBean.getListener(listener);
skipListeners.add((SkipListener<I, O>) stepListener);
}

@SuppressWarnings("unchecked")
SimpleStepBuilder<I, O> result = this;
return result;
return this;
}


/**
* Register a skip listener.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
*
* @author Dave Syer
* @author Mahmoud Ben Hassine
* @author Alexei Klenin
*
* @since 2.2
*/
Expand Down Expand Up @@ -265,7 +266,6 @@ public SimpleStepBuilder<I, O> readerIsTransactionalQueue() {
* @param listener the object that has a method configured with listener annotation
* @return this for fluent chaining
*/
@SuppressWarnings("unchecked")
@Override
public SimpleStepBuilder<I, O> listener(Object listener) {
super.listener(listener);
Expand All @@ -281,18 +281,14 @@ public SimpleStepBuilder<I, O> listener(Object listener) {
itemListenerMethods.addAll(ReflectionUtils.findMethod(listener.getClass(), OnProcessError.class));
itemListenerMethods.addAll(ReflectionUtils.findMethod(listener.getClass(), OnWriteError.class));

if(itemListenerMethods.size() > 0) {
StepListenerFactoryBean factory = new StepListenerFactoryBean();
factory.setDelegate(listener);
itemListeners.add((StepListener) factory.getObject());
if (!itemListenerMethods.isEmpty()) {
StepListener stepListener = StepListenerFactoryBean.getListener(listener);
itemListeners.add(stepListener);
}

@SuppressWarnings("unchecked")
SimpleStepBuilder<I, O> result = this;
return result;
return this;
}


/**
* Register an item reader listener.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.springframework.batch.core.Step;
import org.springframework.batch.core.StepExecutionListener;
import org.springframework.batch.core.StepListener;
import org.springframework.batch.core.annotation.AfterStep;
import org.springframework.batch.core.annotation.BeforeStep;
import org.springframework.batch.core.listener.StepListenerFactoryBean;
Expand All @@ -40,6 +42,7 @@
*
* @author Dave Syer
* @author Michael Minella
* @author Alexei Klenin
*
* @since 2.2
*/
Expand Down Expand Up @@ -91,14 +94,15 @@ public B startLimit(int startLimit) {
* @return this for fluent chaining
*/
public B listener(Object listener) {
StepListenerFactoryBean.assertListener(listener);

Set<Method> stepExecutionListenerMethods = new HashSet<>();
stepExecutionListenerMethods.addAll(ReflectionUtils.findMethod(listener.getClass(), BeforeStep.class));
stepExecutionListenerMethods.addAll(ReflectionUtils.findMethod(listener.getClass(), AfterStep.class));

if(stepExecutionListenerMethods.size() > 0) {
StepListenerFactoryBean factory = new StepListenerFactoryBean();
factory.setDelegate(listener);
properties.addStepExecutionListener((StepExecutionListener) factory.getObject());
if (!stepExecutionListenerMethods.isEmpty()) {
StepListener stepListener = StepListenerFactoryBean.getListener(listener);
properties.addStepExecutionListener((StepExecutionListener) stepListener);
}

@SuppressWarnings("unchecked")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,12 @@
*/
package org.springframework.batch.core.configuration.xml;

import org.springframework.batch.core.StepExecution;
import org.springframework.batch.core.annotation.BeforeStep;

/**
* @author Dave Syer
* @author Alexei Klenin
* @since 2.1.2
*/
public class DummyPojoStepExecutionListener extends AbstractTestComponent {
Expand All @@ -26,4 +29,8 @@ public void execute() {
executed = true;
}

@BeforeStep
public void beforeStep(StepExecution execution) {
}

}
Loading