Embracing the Messiness in Search of Epic Solutions

Spring: Set Active Profile Using Existing JNDI

Posted

in

If we are using @Profile, Spring allows us select the active profile by using spring.profiles.active key. Although I can hardcode the active profile in web.xml, I want my active profile to be determined based on a JNDI string defined in the application server.

PROBLEM

My application servers (DEV, QA and PROD) have an existing JNDI string (let’s assume it is called cell/persistent/env) that allows the deployed application to check what environment it is currently in.

I’m very reluctant to create yet another JNDI called spring.profiles.active that does the exact same thing as this existing JNDI, but it allows Spring to seamlessly activate the right profile.

There are two solutions to this problem…

SOLUTION 1: Creating Application-specific JNDI

One way to do this is to create an application-specific JNDI called spring.profiles.active. Then, we map this JNDI to the existing JNDI called cell/persistent/env.

The problem with this approach is the JNDI mapping configuration is very specific to the application server(s). For example, Websphere Application Server uses ibm-web-bnd.xml, Jetty uses jetty-web.xml, JBoss uses jboss-web.xml, etc… and the configuration is slightly different in every file. The last thing I want to maintain a set of server-specific configuration files.

SOLUTION 2: Creating a Custom ApplicationContextInitializer

Spring provides ApplicationContextInitializer that allows us to programatically initialize the application context. This way, we can perform a JNDI lookup to activate the right profile before refreshing the application context.

First, we need to define a context parameter called contextInitializerClasses in web.xml:-

<web-app ...>
    ...

    <context-param>
        <param-name>contextInitializerClasses</param-name>
        <param-value>com.choonchernlim.epicapp.config.CustomApplicationContextInitializer</param-value>
    </context-param>
</web-app>

Then, we go ahead and implement that custom context initializer class:-

public class CustomApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
    private final JndiLocatorDelegate jndi = JndiLocatorDelegate.createDefaultResourceRefLocator();

    @Override
    public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
        try {
            String profile = jndi.lookup("cell/persistent/env", String.class);
            configurableApplicationContext.getEnvironment().addActiveProfile(profile);
        }
        catch (NamingException e) {
            throw new RuntimeException("JNDI lookup failed", e);
        }
    }
}

Let’s assume the active profile value can either be “DEV”, “QA” or “PROD”.

With that, we can now create environment-specific configurations, and only one configuration will be enabled depending on the JNDI value:-

@Configuration
public class WebServiceURLConfiguration {

    @Profile("DEV")
    @Bean(name = "webServiceURL")
    public String devWebServiceURL() {
        return "http://dev.server:7777/webservice";
    }

    @Profile("QA")
    @Bean(name = "webServiceURL")
    public String qaWebServiceURL() {
        return "http://qa.server:8888/webservice";
    }

    @Profile("PROD")
    @Bean(name = "webServiceURL")
    public String prodWebServiceURL() {
        return "http://prod.server:9999/webservice";
    }
}

Tags:

Comments

Leave a Reply