-
Notifications
You must be signed in to change notification settings - Fork 2.4k
ItemStream is not recognized on scoped steps #4026
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
Comments
My usecase:
UPD: So far I actually only have one step, so, for now, I decided to just drop the thread local, maybe it's not such a big deal since I'm reading the string from memory. But it might be useful if I get hundreds of messages and multiple steps. For now, just returning |
The problem is that the proxy is created very early in the process by Spring (not Spring Batch) and if you want to figure the type out you would need to instantiate the beans which is something you want to prevent. Now especially in your case where you want to switch this might lead to instantiating multiple beans which you don't want. Next it isn't possible as well as the What you could do is create an Or find another way to kick-off the batch job as i'm not sure using a |
Thanks for the suggestions, I'll try them out for sure! About no-op, I even thought of myself. I'll take a look if Spring Integration can persist incoming messages. I figured out how to resolve the real type. With this code, the example works: @Bean
public TaskletStep step(
ItemReader<Map<String, Object>> srcEntityReader,
ObjectProvider<TaskletStep> stepProvider
) {
return steps.get("step")
.<Map<String, Object>, Map<String, Object>>chunk(1)
.reader(srcEntityReader)
.writer(srcEntities -> srcEntities.forEach(srcEntity -> System.out.println("writing " + srcEntity)))
.listener(new StepExecutionListener() {
@Override
public void beforeStep(StepExecution stepExecution) {
if (srcEntityReader instanceof Advised) {
var realReader = ((Advised) srcEntityReader).getTargetSource().getTarget(); //try catch for getTarget
if (realReader instanceof ItemStream) {
stepProvider.getObject().registerStream(((ItemStream) realReader));
}
}
}
@Override
public ExitStatus afterStep(StepExecution stepExecution) {
return null;
}
})
.build();
}
|
With one problem that the scoped proxy is initialized early on, which wouldn't allow for changing the reader from different executions (unless you fully restart the job). |
I'm fairly new to spring batch, can you say a couple of keywords I can search about changing the reader from executions? Can't find anything about it. |
In your own question you state that you switch the reader based on a So it doesn't have to be a problem if you are reloading the configuration for each launch of the job, however if this configuration is part of a larger application that keeps running you do have an issue. |
Right, makes sense. So, the step should be |
In general case though, I think we can assume people don't supply dynamic readers, so this listener logic can still help if user declared And it can also help remove this log that I currently see: o.s.b.c.l.AbstractListenerFactoryBean : org.springframework.batch.item.ItemReader 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. |
Off-topic, about the suggestions. I actually can not only get data by polling an external system but also by accepting an HTTP request from an external system, for which it will hang until the job is complete and give the calculated response. The job should be done in under 3-5 seconds (a 3rd party backend will wait, not human). So, I think introducing an internal channel for incoming data will make responding to HTTP request more complex than just calling I already wrote a second step in the job that relies on the resulting data written by the previous step. According to the official docs, I'm supposed to save it into I think for the sake of consistency, it's either I'm passing data through execution context and job parameters, or I'm introducing 2 internal channels for passing stuff between steps and for starting the job. I think not involving channels will make the code simpler. |
@Sam-Kruglov Based on your comment here: #4026 (comment), should we consider this issue to be solved?
I just wanted to add that the proxy is created based on the return type of the bean definition method, so the return type should be at least an
For support and off-topic discussions, please use StackOverflow. Thank you. |
@fmbenhassine thanks for coming back to this!
|
Thank you for your feedback.
I don't think we need to make Spring Batch extract the advised target from the proxy and register it as a stream, just for the user to not adjust the return type of the bean definition method as documented. As you mentioned
Re-reading your use case, I see no need to use Closing this issue since the proxy is registered as expected when the return type of the bean definition method is defined as documented. |
Uh oh!
There was an error while loading. Please reload this page.
Bug description
If I declare a
@StepScope @Bean
method with return typeJsonItemReader
, it will get wrapped in a lazy proxy. In this case,instanceof ItemStream
returnstrue
andopen()
is called.If I declare a
@StepScope @Bean
method with return typeItemReader
, it will get wrapped in a lazy proxy. In this case,instanceof ItemStream
returnsfalse
andopen()
is not called.Code reference:
spring-batch/spring-batch-core/src/main/java/org/springframework/batch/core/step/builder/SimpleStepBuilder.java
Lines 401 to 407 in 2a9904b
The proxy is created due to
@StepScope
annotation. The whole bean method gets turned into a lazy object that is only evaluated during the job run, so that it is able to inject job parameters into it. So, there is no way for the job to know in advance the exact type of the final object. The solution would be to inspect every step during job runtime and see if it's an instance ofItemStream
or not.Environment
Spring Batch 4.3.3
Spring Boot 2.5.0
Java 11
Minimal Complete Reproducible example
The text was updated successfully, but these errors were encountered: