Description
Say you implement a skip listener like so in Kotlin:
class LoggingSkipListener<I, O>() : SkipListener<I, O> {
override fun onSkipInRead(t: Throwable) {}
override fun onSkipInWrite(item: O, t: Throwable) {}
override fun onSkipInProcess(item: I, t: Throwable) {}
}
in your step, you accidentally use listener
before faultTolerant
:
// WHOOPS - this calls listener(Object) rather than listener(SkipListener)
.listener(LoggingSkipListener(inputKeyExtractor, outputKeyExtractor))
.faultTolerant()
Which results in SimpleStepBuilder.listener(Object)
being called rather than FaultTolerantStepBuilder.listener(SkipListener)
.
You realize your mistake and fix it:
.faultTolerant()
.listener(LoggingSkipListener(inputKeyExtractor, outputKeyExtractor))
Now it works. But damn, your I, O
results in item
being nullable. To fix this, you change them to I : Any, O : Any
.
This will again result in listener(Object)
being used (don't ask me why) but you don't notice it.
This is problematic because listener(Object)
doesn't know the interface SkipListener
and there are no annotated methods. Therefore, your listener is silently not registered anymore.
It seems that this would be easily fixable with something like
if (listener instanceof SkipListener) {
skipListeners.add((SkipListener<I, O>) listener);
return this;
}
I'd even go a step further and prevent listeners being passed that don't get registered:
if (listener instanceof SkipListener) {
skipListeners.add((SkipListener<I, O>) listener);
return this;
}
...
if (skipListenerMethods.isEmpty()) {
throw new IllegalArgumentException("No skip listener methods found in: " + listener);
}
Activity
[-]Make listener API less fragile[/-][+]Make listener API more fragile[/+][-]Make listener API more fragile[/-][+]Make listener API less error prone[/+]fmbenhassine commentedon May 22, 2025
Thank you for reporting this issue! I prefer the fail fast approach: if the listener is not annotated as expected, it should be rejected with an exception rather than silently ignored.
Contributions are welcome!
Fix listener API allowing invalid listeners
SoxerL commentedon Jun 21, 2025
I tried to fix this in the implementation.
There is now a list of errors that is filled when there is a listener registration that does not use any of the annotations provided. When calling build() and there are any listener errors the method will throw a BuilderException.
There is one Tests I disabled, where I am not sure how it should be fixed. StepBuilderTests.testAnnotationBasedChunkListenerForJobStepBuilder as the test tries to create a chunkListener directly in a StepBuilderHelper, which as far as I could understand, does not check for any chunkListener methods and therefore this should not work by default.
I am happy to adapt my implementation if you have any guidance on how to solve this issue.
Fix listener API allowing invalid listeners
Fix listener API allowing invalid listeners