Announcement Announcement Module
Collapse
No announcement yet.
(Somewhat lenient) validation of spring app context Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • (Somewhat lenient) validation of spring app context

    Short version:
    How to smoothly validate that a spring app ctx XML file is parseable and complete in the sense that all bean references could be resolved?
    Is it for instance possible to bypass/disable init-methods (and messageSource) when initializing a ClasspathXmlApplicationContext?

    Problem description:
    What I want to do is to test that a spring app context is valid (defined as above) (see *: below for a more detailed motivation).

    Until now, we have done the validation test by simply instantiating a ClasspathXmlApplicationContext(ourRootSpringXmlFil e)

    This has worked great for a long time, but because of the introduction of a datasource bean (whose implementation is propretiary, has certain problematic infrastructure dependencies and is outside our control) the test will now fail. It fails because ClasspathXmlApplicationContext calls "refresh" when instantiated, which will cause hibernate sessionFactory to be set up and demand connections from our datasource bean (thereby trigging the failure due to the datasource bean's infrastructure dependencies).

    So, we can no longer just do a normal instantiation of ClasspathXmlApplicationContext for our test, because refresh's invocation of the init-methods will cause a failure.

    Thus, we have to relax our test a little bit.

    I have tried GenericApplicationContext, but to my perception it looks like it just reads bean definitions (no bean reference resolving).
    It looks to me that AbstractApplicationContext.refresh() is the "problem".
    I believe what we need is something in between a GenericAppCtx and a ClasspathXmlAppCtx, a sort of "partial refresh()".

    Is there a smooth way to achieve this? Bypassing the init-methods?
    I have tried to create dummy subclasses of *ApplicationContext in a few variants to bypass the init-methods, but I don't know if I can call it "smooth", and I have some problems with a messageSource/messageSourceInitializer (custom but which I cannot control) that has started to fail under my init-method bypass "hacking".

    Any suggestions?

    *: Motivation for the test. Our system's spring config consists of two parts - a static basic config and a environment specific part. All our tests use an environment specific part that is test-specific. However, the artifact we deploy to our app server, has a spring config where the environment specific part is different and have never been tested. This may be error prone, so we decided to establish a minimal test of the spring app context actually being deployed.

  • #2
    Does it have to be an ApplicationContext, or would an XmlBeanFactory do it? A BeanFactory does not have a refresh operation, and there is no MessageSource.

    If you need ApplicationContext you could consider partitioning your context into a parent (common to all environments, that you can test in your "old" way), and a child with the tricky environment-specific stuff.

    Comment


    • #3
      Thanks for the reply.

      No, an app ctx is not required. Anything that can validate a spring ctx according to my definition of valid (see "short version:" above) will do.
      Yes, I have tried the XmlBeanFactory path.
      It seems to me that it will validate that the spring config is parseable all right, but is it possible to validate the bean references in a smooth way?

      Actually, I seem to be able to validate bean references using code as follows:

      Code:
              
      XmlBeanFactory factory = new XmlBeanFactory(
          new ClassPathResource("our-spring-config.xml"));
      String[] names = factory.getBeanDefinitionNames();
      for (int i = 0; i < names.length; i++) {
          BeanDefinition beanDef = factory.getBeanDefinition(names[i]);
          PropertyValue[] props = beanDef.getPropertyValues().getPropertyValues();
          for (int j = 0; j < props.length; j++) {
              System.out.println("Here is a property with name " + 
                  props[j].getName() + " with value " + props[j].getValue());
          }
      }
      It looks to me like the "props[j].getValue()" will output values such as "<aBeanName>" when a property is a bean reference, and "someValue" when a property is not a bean reference. Thus, I could use this to validate bean reference (by looking up a factory.getBeanDefinition("aBeanName") each time a "<aBeanName>" property value is encountered).
      However, for me this smells of working by coincidence based on internal spring implementation details and if there is no smoother way to do it, I think we need to reconsider the trade-off between having the test and introducing such ugly code to support it in the first place.

      Hopefully though, there is a better way to validate bean references when using a XmlBeanFactory?

      Comment


      • #4
        In a nutshell: what you want is to be able to validate that all bean references defined in a set of xml files resolve to bean definitions in the same set. You don't want to instantiate the beans, just check if the references can be resolved. Is that correct?

        If you don't want to actually instantiate the beans, then I think you are going to have to go to some lengths (some would call it ugly), to achieve this. You could try raising an issue in JIRA.

        Another idea: you could always try an xsl validator - the DTD is public and very stable. Might be quite succint. But everyone hates xsl.

        Personally, I would try to break up the context into a sensible hierarchy, and test the independent parts of that by actually instantiating the beans. That way Spring does the work.

        Comment


        • #5
          Originally posted by david_syer
          In a nutshell: what you want is to be able to validate that all bean references defined in a set of xml files resolve to bean definitions in the same set. You don't want to instantiate the beans, just check if the references can be resolved. Is that correct?
          You are correct. Thanks for the reply. My code sample is almost there I think, but relying on the "<aBeanName>"-convention is ugly and make me hesitate.
          XSL is an interesting thought, but as you say, I am a developer who basically hates XSL

          Comment


          • #6
            My code sample were almost there indeed. I had to leave this for a while, but
            with a small modification I was able to do what I wanted without having to rely on anything particular "ugly".

            The basic idea is
            1. Parse the spring-config.xml using XmlBeanFactory (which will ensure well-formedness and validity with respect to spring DTD)
            2. Loop through all bean defs, and for each bean def:
            Look up all properties, and for each property:
            If the property is a reference to another bean, check whether a bean def exists for the referenced bean in the XmlBeanFactory
            Step 2. will validate that bean references that all required beans are present.

            Of course the test will not catch runtime problems, but it is good enough to catch the most common errors.

            Code:
            XmlBeanFactory factory = 
                new XmlBeanFactory(new ClassPathResource("our-spring-config.xml"));
            
            String[] names = factory.getBeanDefinitionNames();
            
            for (int i = 0; i < names.length; i++) {
                BeanDefinition beanDef = factory.getBeanDefinition(names[i]);
                PropertyValue[] props = beanDef.getPropertyValues().getPropertyValues();
                for (int j = 0; j < props.length; j++) {
                    if (props[j].getValue() instanceof RuntimeBeanReference) {
                        RuntimeBeanReference beanReference = 
                            (RuntimeBeanReference)props[j].getValue();
                        assertNotNull("Failed to resolve bean reference " + 
                            beanReference.getBeanName(),
                            factory.getBeanDefinition(beanReference.getBeanName()));
                    }
                }
            }

            Comment

            Working...
            X