Multiple Spring Boot Active Profiles in Tomcat

Recently I’ve been handling the deployment of a Spring Boot (1.3.0) project using Gradle and the Cargo plugin to deploy a war file to Tomcat. With some tinkering in Groovy to create myself a prompt for the Tomcat password and prevent a failed undeploy from failing the entire build, I had everything I wanted.

Each machine I was deploying to had 2 environment variables; spring.profiles.active to define which profile to use (stage, prod etc.) and jasypt.encryptor.password to allow the use of Jasypt encrypted properties. This worked great until we branched for the first time and suddenly I needed two deployments on the same machine within the same Tomcat container with different profiles. At this point I began to struggle.

The “active profiles” property can be set in a number of ways ranging jvm args if you’re using the embedded server, to environment variables and web.xml properties. My initial thought was to set it as a context-specific environment variable in Tomcat/conf/Catalina/localhost/stage.xml e.g.

<context docbase="...">    
 <environment name="spring.profiles.active" value="stage" type="java.lang.String" override="false"/>
</context>

This worked and the deploy got hold of its profile-specific properties like a charm but the problem is that Tomcat deletes these files during undeploy so the file disappeared when the next build went up 😦

Next I decided I’d have to break the “location agnostic war” methodology and include a specific web.xml file per build, e.g. for stage I modified the gradle war task like so:

task stageWar(type: War) {
     baseName = 'stage'
     webXml = file('src/stageWeb.xml')
}

…and created stageWeb.xml as follows:

<web-app>
 <context-param>
  <param-name>spring.profiles.active</param-name>
  <param-value>stage</param-value>
 </context-param>
</web-app>

I was now expecting it to work like just as the context xml modification had previously but without getting removed on every build. Sadly, for some reason, using the web.xml method didn’t work and the deploys defaulted to the base profile 😦

For some reason, the property was only available to the servlet context but not the Spring Boot application so I had to modify our SpringBootServletInitializer to pass it over like so:

@SpringBootApplication
public class Application extends SpringBootServletInitializer {

public String profile = null;

@Override
public void onStartup(ServletContext servletContext) throws ServletException {

//Grab the active profile from the servlet conext
profile = servletContext.getInitParameter("spring.profiles.active");
super.onStartup(servletContext);
}

@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {

//...and pass it to the boot application
application.application().setAdditionalProfiles(profile);
return application.sources(Application.class);
}

public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

Now every deploy is aware of its correct active profile and I’m happy 🙂

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Advertisements
%d bloggers like this: