Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit b645523

Browse files
committed
FEATURE: validate step listeners
1 parent 10fd371 commit b645523

File tree

11 files changed

+223
-153
lines changed

11 files changed

+223
-153
lines changed

‎spring-batch-core/src/main/java/org/springframework/batch/core/listener/AbstractListenerFactoryBean.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,9 +214,11 @@ public static boolean isListener(Object target, Class<?> listenerType, ListenerM
214214
if (target == null) {
215215
return false;
216216
}
217+
217218
if (listenerType.isInstance(target)) {
218219
return true;
219220
}
221+
220222
if (target instanceof Advised) {
221223
TargetSource targetSource = ((Advised) target).getTargetSource();
222224
if (targetSource != null && targetSource.getTargetClass() != null
@@ -228,11 +230,21 @@ public static boolean isListener(Object target, Class<?> listenerType, ListenerM
228230
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()));
229231
}
230232
}
233+
231234
for (ListenerMetaData metaData : metaDataValues) {
232-
if (MethodInvokerUtils.getMethodInvokerByAnnotation(metaData.getAnnotation(), target) != null) {
235+
Class<?> listenerInterface = metaData.getListenerInterface();
236+
if (listenerInterface.isInstance(target)) {
233237
return true;
234238
}
239+
240+
if (metaData.getAnnotation() != null) {
241+
MethodInvoker annotatedMethod = getMethodInvokerByAnnotation(metaData.getAnnotation(), target);
242+
if (annotatedMethod != null) {
243+
return true;
244+
}
245+
}
235246
}
247+
236248
return false;
237249
}
238250
}

‎spring-batch-core/src/main/java/org/springframework/batch/core/listener/ListenerMetaData.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,14 @@
2727
*/
2828
public interface ListenerMetaData {
2929

30-
publicString getMethodName();
30+
String getMethodName();
3131

32-
publicClass<? extends Annotation> getAnnotation();
32+
Class<? extends Annotation> getAnnotation();
3333

34-
publicClass<?> getListenerInterface();
34+
Class<?> getListenerInterface();
3535

36-
publicString getPropertyName();
36+
String getPropertyName();
3737

38-
publicClass<?>[] getParamTypes();
38+
Class<?>[] getParamTypes();
3939

4040
}

‎spring-batch-core/src/main/java/org/springframework/batch/core/listener/StepListenerFactoryBean.java

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,34 @@
1515
*/
1616
package org.springframework.batch.core.listener;
1717

18+
import java.util.stream.Collectors;
19+
import java.util.stream.Stream;
20+
1821
import org.springframework.batch.core.StepListener;
22+
import org.springframework.batch.core.jsr.JsrStepListenerMetaData;
1923

2024
/**
2125
* This {@link AbstractListenerFactoryBean} implementation is used to create a
2226
* {@link StepListener}.
2327
*
2428
* @author Lucas Ward
2529
* @author Dan Garrette
30+
* @author Alexei KLENIN
2631
* @since 2.0
2732
* @see AbstractListenerFactoryBean
2833
* @see StepListenerMetaData
2934
*/
3035
public class StepListenerFactoryBean extends AbstractListenerFactoryBean<StepListener> {
36+
private static final String STR_STEP_LISTENER_ANNOTATIONS_LIST = Stream
37+
.of(StepListenerMetaData.values())
38+
.map(StepListenerMetaData::getAnnotation)
39+
.map(Class::getSimpleName)
40+
.map(annotation -> String.format("\t- @%s\n", annotation))
41+
.collect(Collectors.joining());
42+
private static final String ERR_OBJECT_NOT_STEP_LISTENER_TEMPLATE =
43+
"Object of type [%s] is not a valid step listener. " +
44+
"It must ether implement StepListener interface or have methods annotated with any of:\n" +
45+
STR_STEP_LISTENER_ANNOTATIONS_LIST;
3146

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

67+
@Override
68+
public void setDelegate(Object delegate) {
69+
super.setDelegate(assertListener(delegate));
70+
}
71+
5272
/**
5373
* Convenience method to wrap any object and expose the appropriate
5474
* {@link StepListener} interfaces.
@@ -72,6 +92,30 @@ public static StepListener getListener(Object delegate) {
7292
* annotations
7393
*/
7494
public static boolean isListener(Object delegate) {
75-
return AbstractListenerFactoryBean.isListener(delegate, StepListener.class, StepListenerMetaData.values());
95+
ListenerMetaData[] metaDataValues = Stream
96+
.<ListenerMetaData> concat(
97+
Stream.of(StepListenerMetaData.values()),
98+
Stream.of(JsrStepListenerMetaData.values()))
99+
.toArray(ListenerMetaData[]::new);
100+
101+
return AbstractListenerFactoryBean.isListener(delegate, StepListener.class, metaDataValues);
102+
}
103+
104+
/**
105+
* Asserts that {@code delegate} is a valid step listener. If this is not a case, throws an
106+
* {@link IllegalArgumentException} with message detailing the problem. Object is a valid
107+
* step listener is it ether implements interface {@link StepListener} or has any method
108+
* annotated with one of marker annotations.
109+
*
110+
* @param delegate the object to check
111+
* @return valid step execution listener
112+
*/
113+
public static Object assertListener(Object delegate) {
114+
if (!isListener(delegate)) {
115+
throw new IllegalArgumentException(String.format(
116+
ERR_OBJECT_NOT_STEP_LISTENER_TEMPLATE, delegate.getClass().getName()));
117+
}
118+
119+
return delegate;
76120
}
77121
}

‎spring-batch-core/src/main/java/org/springframework/batch/core/step/builder/AbstractTaskletStepBuilder.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import org.springframework.batch.core.ChunkListener;
2424
import org.springframework.batch.core.Step;
2525
import org.springframework.batch.core.StepExecutionListener;
26+
import org.springframework.batch.core.StepListener;
2627
import org.springframework.batch.core.annotation.AfterChunk;
2728
import org.springframework.batch.core.annotation.AfterChunkError;
2829
import org.springframework.batch.core.annotation.BeforeChunk;
@@ -47,6 +48,7 @@
4748
* @author Dave Syer
4849
* @author Michael Minella
4950
* @author Mahmoud Ben Hassine
51+
* @author Alexei Klenin
5052
*
5153
* @since 2.2
5254
*
@@ -159,10 +161,9 @@ public B listener(Object listener) {
159161
chunkListenerMethods.addAll(ReflectionUtils.findMethod(listener.getClass(), AfterChunk.class));
160162
chunkListenerMethods.addAll(ReflectionUtils.findMethod(listener.getClass(), AfterChunkError.class));
161163

162-
if(chunkListenerMethods.size() > 0) {
163-
StepListenerFactoryBean factory = new StepListenerFactoryBean();
164-
factory.setDelegate(listener);
165-
this.listener((ChunkListener) factory.getObject());
164+
if (!chunkListenerMethods.isEmpty()) {
165+
StepListener stepListener = StepListenerFactoryBean.getListener(listener);
166+
this.listener((ChunkListener) stepListener);
166167
}
167168

168169
@SuppressWarnings("unchecked")

‎spring-batch-core/src/main/java/org/springframework/batch/core/step/builder/FaultTolerantStepBuilder.java

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@
9191
* @author Chris Schaefer
9292
* @author Michael Minella
9393
* @author Mahmoud Ben Hassine
94+
* @author Alexei Klenin
9495
*
9596
* @since 2.2
9697
*/
@@ -204,18 +205,14 @@ public SimpleStepBuilder<I, O> listener(Object listener) {
204205
skipListenerMethods.addAll(ReflectionUtils.findMethod(listener.getClass(), OnSkipInProcess.class));
205206
skipListenerMethods.addAll(ReflectionUtils.findMethod(listener.getClass(), OnSkipInWrite.class));
206207

207-
if(skipListenerMethods.size() > 0) {
208-
StepListenerFactoryBean factory = new StepListenerFactoryBean();
209-
factory.setDelegate(listener);
210-
skipListeners.add((SkipListener) factory.getObject());
208+
if (!skipListenerMethods.isEmpty()) {
209+
StepListener stepListener = StepListenerFactoryBean.getListener(listener);
210+
skipListeners.add((SkipListener<I, O>) stepListener);
211211
}
212212

213-
@SuppressWarnings("unchecked")
214-
SimpleStepBuilder<I, O> result = this;
215-
return result;
213+
return this;
216214
}
217215

218-
219216
/**
220217
* Register a skip listener.
221218
*

‎spring-batch-core/src/main/java/org/springframework/batch/core/step/builder/SimpleStepBuilder.java

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
*
6565
* @author Dave Syer
6666
* @author Mahmoud Ben Hassine
67+
* @author Alexei Klenin
6768
*
6869
* @since 2.2
6970
*/
@@ -265,7 +266,6 @@ public SimpleStepBuilder<I, O> readerIsTransactionalQueue() {
265266
* @param listener the object that has a method configured with listener annotation
266267
* @return this for fluent chaining
267268
*/
268-
@SuppressWarnings("unchecked")
269269
@Override
270270
public SimpleStepBuilder<I, O> listener(Object listener) {
271271
super.listener(listener);
@@ -281,18 +281,14 @@ public SimpleStepBuilder<I, O> listener(Object listener) {
281281
itemListenerMethods.addAll(ReflectionUtils.findMethod(listener.getClass(), OnProcessError.class));
282282
itemListenerMethods.addAll(ReflectionUtils.findMethod(listener.getClass(), OnWriteError.class));
283283

284-
if(itemListenerMethods.size() > 0) {
285-
StepListenerFactoryBean factory = new StepListenerFactoryBean();
286-
factory.setDelegate(listener);
287-
itemListeners.add((StepListener) factory.getObject());
284+
if (!itemListenerMethods.isEmpty()) {
285+
StepListener stepListener = StepListenerFactoryBean.getListener(listener);
286+
itemListeners.add(stepListener);
288287
}
289288

290-
@SuppressWarnings("unchecked")
291-
SimpleStepBuilder<I, O> result = this;
292-
return result;
289+
return this;
293290
}
294291

295-
296292
/**
297293
* Register an item reader listener.
298294
*

‎spring-batch-core/src/main/java/org/springframework/batch/core/step/builder/StepBuilderHelper.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@
1717

1818
import org.apache.commons.logging.Log;
1919
import org.apache.commons.logging.LogFactory;
20+
2021
import org.springframework.batch.core.Step;
2122
import org.springframework.batch.core.StepExecutionListener;
23+
import org.springframework.batch.core.StepListener;
2224
import org.springframework.batch.core.annotation.AfterStep;
2325
import org.springframework.batch.core.annotation.BeforeStep;
2426
import org.springframework.batch.core.listener.StepListenerFactoryBean;
@@ -40,6 +42,7 @@
4042
*
4143
* @author Dave Syer
4244
* @author Michael Minella
45+
* @author Alexei Klenin
4346
*
4447
* @since 2.2
4548
*/
@@ -91,14 +94,15 @@ public B startLimit(int startLimit) {
9194
* @return this for fluent chaining
9295
*/
9396
public B listener(Object listener) {
97+
StepListenerFactoryBean.assertListener(listener);
98+
9499
Set<Method> stepExecutionListenerMethods = new HashSet<>();
95100
stepExecutionListenerMethods.addAll(ReflectionUtils.findMethod(listener.getClass(), BeforeStep.class));
96101
stepExecutionListenerMethods.addAll(ReflectionUtils.findMethod(listener.getClass(), AfterStep.class));
97102

98-
if(stepExecutionListenerMethods.size() > 0) {
99-
StepListenerFactoryBean factory = new StepListenerFactoryBean();
100-
factory.setDelegate(listener);
101-
properties.addStepExecutionListener((StepExecutionListener) factory.getObject());
103+
if (!stepExecutionListenerMethods.isEmpty()) {
104+
StepListener stepListener = StepListenerFactoryBean.getListener(listener);
105+
properties.addStepExecutionListener((StepExecutionListener) stepListener);
102106
}
103107

104108
@SuppressWarnings("unchecked")

‎spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/DummyPojoStepExecutionListener.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,12 @@
1515
*/
1616
package org.springframework.batch.core.configuration.xml;
1717

18+
import org.springframework.batch.core.StepExecution;
19+
import org.springframework.batch.core.annotation.BeforeStep;
1820

1921
/**
2022
* @author Dave Syer
23+
* @author Alexei Klenin
2124
* @since 2.1.2
2225
*/
2326
public class DummyPojoStepExecutionListener extends AbstractTestComponent {
@@ -26,4 +29,8 @@ public void execute() {
2629
executed = true;
2730
}
2831

32+
@BeforeStep
33+
public void beforeStep(StepExecution execution) {
34+
}
35+
2936
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /