views:

38

answers:

3

I have a need to change the spring applicationContext.xml file that is used based upon a property, this property MUST be defined somewhere outside of the war file (ie. it can't be in web.xml). Currently, I've arrived at the following solution (see my answer below), wondering if there's a better way to do this?

A: 

there are 4 parts to my solution. first, in web.xml of my application I define the following:

<context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:applicationContext.xml</param-value>
    </context-param>

    <context-param>
        <param-name>contextConfigLocation1</param-name>
        <param-value>classpath:applicationContext-1.xml</param-value>
    </context-param>

    <context-param>
        <param-name>contextConfigLocation2</param-name>
        <param-value>classpath:applicationContext.xml</param-value>
    </context-param>

    <listener>
        <listener-class>com.my.package.MyContextLoaderListener</listener-class>
    </listener>

Then I extend ContextLoaderListener

package com.my.package;

import org.springframework.web.context.ContextLoader;
import org.springframework.web.context.ContextLoaderListener;

public class MyContextLoaderListener extends ContextLoaderListener {
    @Override
    protected ContextLoader createContextLoader() {
        return new MyContextLoader();
    }
}

and ContextLoader

package com.my.package;

import javax.servlet.ServletContext;

import org.springframework.web.context.ConfigurableWebApplicationContext;
import org.springframework.web.context.ContextLoader;

public class LnvContextLoader extends ContextLoader {

    private static final String APP_CONTEXT_PROP = "MY_CONTEXT_LOAD_PARAM";

    @Override
    protected void customizeContext(ServletContext servletContext,
            ConfigurableWebApplicationContext wac) {

        //check for system property first, if not defined, check for env variable
        String appContextParam = System.getProperty(APP_CONTEXT_PROP);
        if(appContextParam==null)
        {
            appContextParam = System.getenv(APP_CONTEXT_PROP);
        }


        if(appContextParam!=null && !appContextParam.equals("")){

            String initParam = servletContext.getInitParameter(appContextParam);

            wac.setConfigLocation(initParam);
        }


    }
}

and finally, in my tomcat startup, I define the environment variable in setenv.bat

set MY_CONTEXT_LOAD_PARAM=contextConfigLocation1

this solution loads it from an environment variable, but the code is flexible and allows it to be set in a system property instead.

shsteimer
A: 

If you load your application context through the classpath, you can override it by placing another version of the applicationContext.xml in the server's classpath.

My solution would be to have a very simple applicationContext, that includes the real application context :

applicationContext.xml :

<beans>
    <import resource="classpath:realContext.xml"/>
</beans>

If you want another context, add an applicationContext.xml to your server's classpath with :

<beans>
    <import resource="classpath:realContext2.xml"/>
</beans>

And have realContext.xml and realContext2.xml packaged in your WAR. No need for fancy context listener.

Just my opinion, but I quite dislike to have WARs that are not self contained. I find it very convinient to have a single unit of deployement. So I would prefer to create 2 different versions of my WAR during the build process, one for each needed configuration.

Another solution, if you want to load a different bean depending on a given property, you can use a PropertyPlaceholderConfigurer and put the name of the bean as a property :

<beans>
  <bean id="bean1" .../>
  <bean id="bean2" .../>
  <bean id="otherBean">
    <property name="injectDifferentBean" ref="${property.containing.bean.name" />
  </bean>
</beans>

and a property file with :

property.containing.bean.name=bean1

or

property.containing.bean.name=bean2
Guillaume
+1  A: 

Have you considered using the beanRefContext approach. (ContextSingletonBeanFactoryLocator). This way you can configure your spring config files (and their names) via another spring config file.

Then you can paramaterise that file by whatever means you deem appropriate and switch the file names that way.

The file looks like this:

<beans>
    <bean id="businessBeanFactory" class="org.springframework.context.support.ClassPathXmlApplicationContext">
        <constructor-arg value="${NameOfBeanConfigFile}" />
    </bean>
</beans>

And you can use PropertyPlaceHolderConfigurer to set the value of NameOfBeanConfigFile.

I like this approach as it means I can mix static bean config file names with dynamic bean config file names and thus don't have to duplicate bean config.

When I had to do a similar thing I would parameterise via a config file loaded as a URL resource (via jndi)

Michael Wiles