Results 1 to 10 of 10

Thread: Selection of only Job and Step BeanDefinitions

  1. #1
    Join Date
    May 2009
    Posts
    18

    Default Selection of only Job and Step BeanDefinitions

    Hi,

    I am looking at using BeanFactoryPostPrtocessors to potentially amend Jobs and Steps according to common/mandatory site requirements. I am happy with actually applying the amendments to Bean Definitions (standard/common set of listeners for instance - thanks to Dan and Dave's help in a previous post), but I am now stuck on generically identifying the BeanDefinitions of Jobs and Step so that I can apply the required amendments.

    I seem to be able to get the definitions for Jobs using getBeanNamesForType(AbstractJob.class) - hopefully that is acceptable. I then did wonder if I could go into these definitions and locate something with regard to its Step names or something, but I am not seeing that. I did also try such things as getBeanNamesForType(AbstractStep.class); getBeanNamesForType(TaskletStep.class); beanFactory.getBeanNamesForType(FactoryBean.class) . AbstractStep and TaskletStep come back with nothing, whilst FactoryBean did come back with a list which included Steps, but it also included such things as '&jobRepository, &mapJobRepository, &jobExplorer' - Note that the names here (and on my Steps mentioned in the same list) are prefixed by '&', whereas the names in other lists (from AbstractJob for instance) are not??

    If I look at the definitions for my Steps they all have a bean class of org.springframework.batch.core.configuration.xml.S tepParserStepFactoryBean, but this does not seem a satisfactory type to use to get the step definitions (and it is not accessible anyway to refer to - not public class).

    So my question is this: Is there a way (around the BeanFactoryPostProcessor point) to generically identify the Jobs and Steps defined within the current ApplicationContext.

    I am using Spring Batch 2.0.0 and Spring 2.5.6

    Thanks

    Simon

  2. #2
    Join Date
    Feb 2008
    Posts
    488

    Default

    Simon,

    I think you are on the right track. Your confusion seems to stem from the fact that you are confusing two different concepts in the creation of the application context; you are trying to work with BeanDefinitions and instantiated objects at the same time. Your work in the BeanFactoryPostProcessor needs to focus squarely on the BeanDefinitions. You will be manipulating information in the BeanFactory before the actual bean objects get instantiated. That way, when they are finally created, they will have the correct definitions.

    When you dig into the BeanFactory, you will be pulling out BeanDefinitions, *not* Steps/Jobs/FactoryBeans or anything else instantiated like that.
    Code:
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        PropertyValues prototypePvs = beanFactory.getBeanDefinition("prototypeStepName").getPropertyValues();
    
        for (String beanName : beanFactory.getBeanDefinitionNames()) {
            BeanDefinition bd = beanFactory.getBeanDefinition(beanName);
            if (isAbstractStep(bd, beanFactory)) {
                MutablePropertyValues pvs = (MutablePropertyValues) bd.getPropertyValues();
                //
                // Here is where you should pull information out of 'prototypePvs' 
                // and inject into 'pvs'
                //
            }
        }
    }
    
    /**
     * @param bd
     * @param beanFactory
     * @return TRUE if the bean represents an AbstractStep (or
     *         StepParserStepFactoryBean).
     */
    private boolean isAbstractStep(BeanDefinition bd, ConfigurableListableBeanFactory beanFactory) {
        Class<?> stepClass = getClass(bd, beanFactory);
        return StepParserStepFactoryBean.class.isAssignableFrom(stepClass)
                || AbstractStep.class.isAssignableFrom(stepClass);
    }
    I actually just implemented something like this two days ago to fix a bug for the 2.0.1 release. Check out https://fisheye.springsource.org/bro...Processor.java to get an idea of what you may need.

    As a side note, the '&' that appears in front of the FactoryBean name is automatically put there by Spring. For example, the StepParserStepFactoryBean is used to create a Step. You configure the FactoryBean in your XML, but you need the Step to be in the ApplicationContext as well. If the 'id' of the FactoryBean is "step1", then we want the Step's name to be "step1". You can see that this could cause a conflict since you have two different beans with the same name. So Spring changes the FactoryBean's id to "&step1" to differentiate it from the Step itself.
    Last edited by DHGarrette; May 21st, 2009 at 09:26 AM.

  3. #3
    Join Date
    May 2009
    Posts
    18

    Default

    Dan,

    Thanks for a really helpful reply. I think that the operative word in your first paragraph is ‘squarely’. Whilst I knew that the BeanFactoryPostProcessor deals with the definitions of beans (e.g. the recipe used to create/instantiate them), here I failed to go that step further and tried to relate them to instantiated types (Job etc), for some strange reason!!

    I checked out your latest amendment as suggested and have a prototype version working now i.e. I can now get hold of the definitions for the Beans which represent Jobs and Steps.

    But I have had to play a bit dirty, due to the accessibility of StepParserStepFactoryBean to us mere non-Spring developers. It is not a public class and so I have had to cheat and put my BeanFactoryPostProcessor into a package of the same name to be able to access it and have ‘isAssignableFrom’ work as expected. Is there another way around this or do I just stay dirty!

    Also, I am assuming that you are referring to our previous post (http://forum.springsource.org/showthread.php?t=71991) when getting the PropertyValues for ‘prototypeStepName’ – is this like the dummy step I mention which hold the changes we want to apply?

    And thanks for the info on ‘&’.

    Thanks again

    Simon

  4. #4
    Join Date
    Feb 2008
    Posts
    488

    Default

    Simon,

    First, I've refactored that code a little bit and pulled my helper methods into a new utility class so that you (and others) can use them in your own FactoryBeanPostProcessors:
    https://fisheye.springsource.org/cha...batch/?cs=3357

    I'm not sure that you really need access to the StepParserStepFactoryBean. The reason it's not public is that we don't want people using it directly (we have more user-friendly *StepFactoryBean classes for that); it's just needed to make the namespace parser work correctly. The reason I don't think you need it is that you should be working only with the BeanDefinition, not the FactoryBean that will be generated.

    I was indeed referring to your previous post when I put in the bit about the "prototype". If you use that approach, you should be able to do things like:
    Code:
    PropertyValue pv = prototypePvs.getPropertyValue("somePropertyOnThePrototype");
    pvs.addPropertyValue(pv);
    No reference to the actual type (*FactoryBean) needed. Unless you've got a different idea?

  5. #5
    Join Date
    May 2009
    Posts
    18

    Default

    Yep – sorry for the confusion with StepParserStepFactoryBean. In my prototype I had a copy of something like your ‘isAbstractStep’ which obviously uses StepParserStepFactoryBean. But now you have moved it out to its own public ‘Utils’ class I can just refer to that (and so not refer to StepParserStepFactoryBean in any of my methods anymore).

    I have no different ideas – I agree. I have those two lines!

    Cheers again,

    Simon

  6. #6
    Join Date
    Feb 2008
    Posts
    488

    Default

    Ah, I see now. Let me know if you run into any other potential issues. I'd like to get all of our ideas in before we release 2.0.1 next week.

  7. #7
    Join Date
    May 2009
    Posts
    18

    Default

    Dan,

    This might be me being fussy, so just say so, but could you have an 'isAbstractJob' method too? - which does the extra frills (search parents for class etc).

    Thanks

    Simon

  8. #8
    Join Date
    Feb 2008
    Posts
    488

    Default

    I made some changes. The isTypeMatch() method of BeanFactory is a better alternative since it is able to check not only the bean's class, but the class that will result from a FactoryBean.
    Code:
    beanFactory.isTypeMatch(beanName, AbstractStep.class)
    Using this method, the PostProcessor wont be dependent on the particular way in which a user configures a Step (ie, the Step subclass or FactoryBean chosen won't matter).

    See here for the changes: https://fisheye.springsource.org/cha...batch/?cs=3370

  9. #9
    Join Date
    May 2009
    Posts
    18

    Default

    Dan thanks for that.

    When I first tried it out, it didn't work for me!! I seemed to be back at the beginning of this post. But then I realised that I had not picked up your new SimpleStepFactoryBean and StepParserStepFactoryBean sources. And again - Voila! It now works a treat (I look for AbstractStep and AbstractJob to get all my required definitions)

    Will these changes be able to make into that imminent next release you mentioned (2.0.1??)

    Thanks for this again.

    Simon

  10. #10
    Join Date
    Feb 2008
    Posts
    488

    Default

    I'm glad it worked out. And yes, this will be part of 2.0.1.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •