This forum is now a read-only archive. All commenting, posting, registration services have been turned off. Those needing community support and/or wanting to ask questions should refer to the Tag/Forum map, and to http://spring.io/questions for a curated list of stackoverflow tags that Pivotal engineers, and the community, monitor.

Programmatically attach Skip/ItemRead/ItemWrite/ItemProcessorListener

We are looking at providing some local, common services to applications running in Spring Batch.

We will provide and own the 'Runner' used to run the Jobs and so will prepare an Application Context etc proir to running the job.

Obvioulsy, we want to minmise the impact of our implementation on the application. The less configuration that they have to do to enable our services the better. We will be using Spring AOP to 'get in, under the covers' at approriate points to perform/provide required activities/services. The other area which is of use to us, in terms of 'getting in under the covers', is through Listeners. But, ideally, we do not want every Step and Job to configure in our set of Listeners (we are aware of 'parents' and 'inheritting', but we would like to not even have to suggest that approach). We would like to attach the Listeners programmatically, and believe it should be possible. Using the Application Context, we can attached our common Listeners to JobExecution, Step and Chunk (using getJob and registerStepExecutionListener (onAbstractJob) and StepListenerFactoryBean for instance), e.g.

Comment

I had seen SimpleStepFactoryBean, but seemed to run into a dead end. In SimpleStepFactoryBean, the listeners (configured in the SimpleStepFactoryBean) on the factorybean are registered with a ChunkProvider and ChunkProcessor as appropriate (different types of listener are registered with either the processor or provider). The chunkprovider/processor are associated with a ChunkOrientedTasklet which is set as the Tasklet for the (Tasklet)Step.

Ideally, I need to get hold of the Tasklet on the (Tasklet)Step or the ChunkProcessor/Provider on each (Tasklet)Step's Tasklet and register my listeners with them (just adding to the existing ones). But this does not seem possible. I could replace the ChunkProcessor/Provider, but I would need the ItemReader/Writer etc, and I can not seem to get them either (... and if I could get them then I would be able to get the ChunkProcessor and Provide, as they are attributes of them!!).

Is there any way that you can think of that I can get hold of the processor and provider? Or should I be looking in another direction or at another approach? Do you think that it is possible to do what I want - by hook or by crook?

Thanks again

Simon

Comment

BeanFactoryPostProcessor might be cleaner. You could add properties directly to the StepFactoryBean that way. N.B. FactoryBeans are generally considered to be internal implementation details, and we might refactpr them more aggressively than more public APIs.

Comment

Dave - Thanks for that. It sounds like the 'right' way, taking note of the fact that their use is not the normal external useage.

Having said that, I have had a good go at it, but I must be doing something silly and going down the wrong paths.

When run, the DefaultListableBeanFactory given to me does say that is used for defining the Steps I am interested in. But I have to admit to be stumped as to what to do next. If I get the BeanDefintion for my steps I can still not see where to go.

The BeanDefintion does seem to have a factoryBeanName attribute, but it is null. It also has a beanClass attribute, which is set to StepParserStepFactoryBean. This seems to wrap SimpleStepFactoryBean (and FaultTolerantStepFactoryBean). StepParserStepFactoryBean allows me to set listeners, but this overwrites any that might already be set on the bean, as opposed to add my new ones, and it does not allow me to get the existing set of listeners so that I could augment them. BUt anyway, it is the class not an actual StepParserStepFactoryBean.

I apologise for asking for more help, but can you? or am I barking up the wrong tree?

Thanks

Simon

Comment

BeanDefinition has getPropertyValues(). You need to grab that and extract the listeners property if it exists, then add your extra ones. You are writing a framework here so expect it to be quite a low level experience. Otherwise you could always just provide your users with a parent bean definition.

Comment

Dave, once again thanks for your time and patience - seems so obvious now you have said it! I'll let you know how I get on.

Agreed, there is always the fallback of parent beans, but, as you suspect, our code is to be used by a number of applications, and the less we ask them to do on our behalf the better (they can either forget, or think they don't have to!)

Thanks again

Simon

Comment

I am now successfully running a prototype that adds our common Listeners. Coding the new StepListener up was a bit long winded (not a simple as delaing with a boolean property!) - GenericBeanDefinitions; RootBeanDefinitions; and more PropertyValues. But doing it this way has helped me understand more.

I might look at configuring a dummy Job and Step in our root Application Context with the required Listeners (and other default/common/mandatory attribute settings I suppose); use a post processor on that Bean purely to extract these default attributes entries, so that I can just attach/add them to the application Jobs/Steps in the child Application Context - save a lot of code on our part in setting these things up, let Spring do it for us. I'll see.