Announcement Announcement Module
Collapse
No announcement yet.
Best Practices for instantiating application contexts Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Best Practices for instantiating application contexts

    G'day,

    I'm keen to hear from the wider community how they handle the dual requirements of:
    1. an entire application sharing a single application context
    2. allowing variability in the application context between environments (dev, QA, prod), without requiring any kind of rebuild or repackaging process during deployment

    The first requirement is handled by ContextSingletonBeanFactoryLocator, but I haven't seen much discussion around the second requirement.

    My current solution involves a custom class (similar in concept to ContextSingletonBeanFactoryLocator) that looks for a system property containing the name(s) of the application context files to load. This allows me to bundle all of my environment specific application context files in the JARs that make up my application, then selectively load the ones that are relevant to each environment by varying the system property per environment.

    However even this minimal approach has become too invasive for some recent projects - in one case I'm not able to set a system property and hence my class is unable to determine which application contexts to load.

    I have a few ideas for possible solutions but before I launch in and implement something I was keen to hear whether the wider community had already solved this problem?

    I've spent some time scanning the forums and read quite a few posts regarding "bootstrapping" application contexts, but I couldn't see anything that dealt with environment variability. My apologies in advance if I missed posts that deal with this topic.

    Cheers,
    Peter

  • #2
    Proposal for RuntimeEnvironmentPropertiesConfigurer.

    I recently submitted a proposal and a reference implementation for this here

    My context files tend to be environment-agnostic, with properties to define environment specific values.

    Comment


    • #3
      G'day Chris,

      Thanks for the prompt response.

      As best I can tell your approach is conceptually identical to the one I'm already using - it reads a system property to determine which environment the code is running in, and then uses that information to work out which application context(s) / properties to load.

      Although this approach has worked well for me in the past I'm now in the situation where I'm unable to control the system properties for the JVM that my code is running in. In this case I need the "environment key" to come from somewhere else, ideally from some environmental information that's available to any JVM, regardless of how it was started.

      I should also mention that it's inappropriate to have the callers of the loader provide the environment key, since that distributes the problem (thereby making it worse).

      My current thinking is to use the hostname of the machine (obtained from java.net.InetAddress) and using that as the key. This is probably overkill in some cases (eg. all nodes in a production cluster may have identical application contexts), but that's easy to remedy by <import>ing the correct environment context from each of the host-specific contexts.

      Cheers,
      Peter

      Comment


      • #4
        Best Practices for instantiating application contexts

        Originally posted by pmonks

        As best I can tell your approach is conceptually identical to the one I'm already using - it reads a system property to determine which environment the code is running in, and then uses that information to work out which application context(s) / properties to load.
        Pretty much; it doesn't load different contexts, simply different properties files. For example, the below snippet configures the message source to have different reload intervals in different environments (1 second in dev, no reload elsewhere).

        Code:
        <bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
                <property name="basename" value="/WEB-INF/messages/messages" />
                <property name="cacheSeconds" value="${frilista.messageCacheSeconds}" />
                <property name="fallbackToSystemLocale" value="false" />
            </bean>
        Originally posted by pmonks
        Although this approach has worked well for me in the past I'm now in the situation where I'm unable to control the system properties for the JVM that my code is running in. In this case I need the "environment key" to come from somewhere else, ideally from some environmental information that's available to any JVM, regardless of how it was started.

        I should also mention that it's inappropriate to have the callers of the loader provide the environment key, since that distributes the problem (thereby making it worse).

        My current thinking is to use the hostname of the machine (obtained from java.net.InetAddress) and using that as the key. This is probably overkill in some cases (eg. all nodes in a production cluster may have identical application contexts), but that's easy to remedy by <import>ing the correct environment context from each of the host-specific contexts.
        It should be possible to extend the RuntimeEnvironmentPropertiesConfigurer to have a pluggable RuntimeEnvironmentKeyResolver; if none is specified, it behaves as it does today. This would allow one to create their own implementation that determines the runtime environment key in an appropriate manner.

        A couple of other approaches to consider (these may not be available to you):

        1. Place the contexts in separate JARs, and only deploy the relevant JAR along with the app; this would allow you to do something like 'classpath:conf/**/*-context.xml'
        2. Adjust the classpath of your application to include a folder on the filesystem; place the environment specific contexts there and reference them via classpath:environment/**/*-context.xml
        3. Similiar to 2; instead of placing the contexts there, implement a custom RuntimeEnvironmentKeyResolver that reads the environment from a file in the folder.

        I'll have a v2 of the RuntimeEnvironmentPropertiesConfigurer with the pluggable key resolution available over the weekend (attached to the JIRA issue).

        Thanks,

        Chris.

        Comment


        • #5
          I suggest extending the concept even farther:

          - read the configuration name (ie, dev, staging, production) from a text file.
          - read the configuration value from a URL.
          - read the configuration value from a database (ie, provide a datasource and SQL statement)

          Comment


          • #6
            A pluggable RuntimeEnvironmentKeyResolver would certainly allow this to occur.

            The biggest challenge is one of inverse dependencies; how do you instantiate a (relatively) complex service (database) that may depend on property replacement to determine how to replace properties?

            Comment


            • #7
              re: Proposal for RuntimeEnvironmentPropertiesConfigurer.

              G'day again,

              I like the idea of a pluggable environment key resolver, with sensible default behaviour if one isn't provided.

              Once you're done with the properties configurer I'll throw together a context loader that uses the same approach (with the same environment key resolution mechanism and default behaviour) and add it to the JIRA ticket as well. I think being able to load environment-specific properties or application contexts (or both) in a consistent fashion should cover most use cases.

              Cheers,
              Peter

              Comment


              • #8
                Best Practices for instantiating application contexts

                Excellent idea; I agree that environment-specific properties & contexts should cover most (reasonable) use cases.

                I posted the pluggable environment key resolver updates to the JIRA issue last night - enjoy!

                Chris.

                Comment


                • #9
                  re: Proposal for RuntimeEnvironmentPropertiesConfigurer

                  I added the new code to the JIRA ticket. It's the larger of the two "RuntimeEnvironmentPropertiesConfigurer.zip" files. *sigh*

                  I was able to cleanly add the new stuff on top - none of the existing code changed.

                  Cheers,
                  Peter

                  Comment


                  • #10
                    At laaaaast, this issue is going to be covered inside the spring framework.

                    I prefer much more determining the runtime environment with a system property set at JVM startup than rebuilding the whole app with some environment-dependant files being changed by the build process

                    Comment


                    • #11
                      What about something like this?

                      ALFA.some.properties:
                      username=alfaUser

                      BETA.some.properties:
                      username=betaUser

                      PROD.some.properties:
                      username=prodUser

                      If you distribute the three files with your app, and set a parameter (say rt_env), that indicates the environment in the JVM startup, you could obtain the right value for the environment the code is being executed at.

                      I would use a PropertyPlaceholderConfigurer like this:

                      <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.Pr opertyPlaceholderConfigurer">
                      <property name="location">
                      <value>classpath:${rt_env}.some.properties</value>
                      </property>
                      <property name="systemPropertiesModeName">
                      <value>SYSTEM_PROPERTIES_MODE_OVERRIDE</value>
                      </property>
                      </bean>

                      and a bean that uses information from that properties files like this:

                      <bean id="dummyController" class="com.viajar.qa.DummyController">
                      <property name="userName">
                      <value>${username}</value>
                      </property>
                      </bean>

                      For example, if I start the JVM with -Drt_env=ALFA, the value "alfaUser" would be injected in the userName property of the controller.

                      This way, the same .jar or .war can be deployed on different environments without the need to regenerate it just to specify the environment it is supposed to work with. Those .jars/.wars would work everywhere.

                      The point here is to configure the JVM (the tomcat startup script, for example), to specify the environment parameter in the startup. This way you don't depend on the hostname, for example.

                      Comment


                      • #12
                        Hi nimeacuerdo,

                        Code:
                        <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
                          <property name="location">
                            <value>classpath:${rt_env}.some.properties</value>
                          </property>
                          <property name="systemPropertiesModeName">
                            <value>SYSTEM_PROPERTIES_MODE_OVERRIDE</value>
                          </property> 
                        </bean>
                        Would that even work? Can you use a PropertyConfigurer to replace a property of the bean itself? I would think that if this can work, you would at least need a second PropertyPlaceholderConfigurer that has a lower order (i.e. higher precedence) to replace the value (for the properties file name) in the main one.

                        I don't know; never used multiple ones before...

                        -Arthur Loder

                        Comment


                        • #13
                          I know what you mean, this a doubt that arised for me too; but I have tested it

                          Comment


                          • #14
                            How does it work?

                            Hey nimeacuerdo,

                            I guess it would be easier for me to test too, but I just saw something in the PropertyPlaceholderConfigurer code that confuses me:

                            Code:
                            protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess, Properties props)
                                    throws BeansException {                                                                               
                                                                                                                                    
                                BeanDefinitionVisitor visitor = new PlaceholderResolvingBeanDefinitionVisitor(props);                  
                                String[] beanNames = beanFactoryToProcess.getBeanDefinitionNames();                                    
                                for (int i = 0; i < beanNames.length; i++) {                                                           
                                    // Check that we're not parsing our own bean definition,                                              
                                    // to avoid failing on unresolvable placeholders in properties file locations.                        
                                    if (!(beanNames[i].equals(this.beanName) && beanFactoryToProcess.equals(this.beanFactory))) {         
                                        BeanDefinition bd = beanFactoryToProcess.getBeanDefinition(beanNames[i]);                            
                                        try {                                                                                                
                                            visitor.visitBeanDefinition(bd);                                                                    
                                        }                                                                                                    
                                        catch (BeanDefinitionStoreException ex) {                                                            
                                            throw new BeanDefinitionStoreException(bd.getResourceDescription(), beanNames[i], ex.getMessage()); 
                                        }                                                                                                    
                                    }                                                                                                     
                                }                                                                                                      
                            }
                            Doesn't the bold line ensure that the actual PropertyPlaceholderConfigurer bean itself (note that the class implements BeanNameAware) will not have its own placeholders replaced? I would think that this would mean your definition would not work.

                            Confused...

                            -Arthur Loder
                            Last edited by Arthur Loder; Oct 27th, 2006, 08:10 AM.

                            Comment


                            • #15
                              Hi,Arthur Loder

                              I think PropertyPlaceholderConfigurer do not resolve it's locations but done by bean factory's ResourceEditor . the locations actually is Resource type and the classpath\ ${}\ file:...etc can be parsed by ResourceEditor.

                              Comment

                              Working...
                              X