About Gal Levinsky

Graduated B.s.c Information System Engineering on BGU University (2004). Been working on large cloud based ERP application in SAP for 7 years and as a development group manager in affiliation company. Co-founded few startups in the domain of social web.

Spring Profile pattern example

Recently we were introduced with the concept of Spring Profiles.

This concept is an easy configuration differentiators for different deployment environments.

The straight forward use case (which was presented) was to annotate the relevant classes so Spring would load the appropriate class according to the active profile.

However, this approach might not always serve the common use case… often, the configuration keys would be the same and only the values will change per environment.

In this post, I would like to present a pattern to support loading configuration data per environment, without the need to create/maintain multiple classes for each profile (i.e. for each environment).

Throughout the post I would take the DB connection configuration as a sample, assuming we have different DB definitions (e.g. username or connection URL) for each deployment environment.

The main idea is to use one class for loading the configuration (i.e.. one class for DB connection definition) and inject into it the appropriate instance which holds the correct profile configuration data.

For convenience and clarity, the process was divided into 3 phases:

Phase 1: infra preparation
Step 1.1 – create a properties file which contains all configuration data
Step 1.2 – create an annotation for each profile
step 1.3 – make sure the profile is loaded during context loading

Phase 2: implementing the profile pattern
Step 2.1 – create a properties interface
Step 2.2 – create a class for each profile
Step 2.3 – create an abstract file which holds the entire data

Phase 3: using the pattern
Step 3.1 – example for using the pattern

Spring Profile pattern – phase 1: infra preparation

This phase will establish the initial infra for using Spring Profile and the configuration files.

Step 1.1 – create a properties file which contains all configuration data

Assuming you have a maven style project, create a file in src/main/resources/properties for each environment, e.g:

my_company_dev.properties
my_company_test.properties
my_company_production.properties

example for my_company_dev.properties content:

jdbc.url=jdbc:mysql://localhost:3306/my_project_db
db.username=dev1
db.password=dev1
hibernate.show_sql=true

example for my_company_production.properties content:

jdbc.url=jdbc:mysql://10.26.26.26:3306/my_project_db
db.username=prod1
db.password=fdasjkladsof8aualwnlulw344uwj9l34
hibernate.show_sql=false

Step 1.2 – create an annotation for each profile

In src.main.java.com.mycompany.annotation create annotation for each Profile, e.g :

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Profile("DEV")
public @interface Dev {
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Profile("PRODUCTION")
public @interface Production {
}

Create an enum for each profile:
public interface MyEnums {

public enum Profile{
DEV,
TEST,
PRODUCTION
}

Step 1.3 – make sure the profile is loaded during context loading

  • Define a system variable to indicate on which environment the code is running.
    In Tomcat, go to ${tomcat.di}/conf/catalina.properties and insert a line:
    profile=DEV (according to your environment)
  • Define a class to set the active profile
    public class ConfigurableApplicationContextInitializer implements
      ApplicationContextInitializer<configurableapplicationcontext> {
    
     @Override
     public void initialize(ConfigurableApplicationContext applicationContext) {
          
      String profile = System.getProperty("profile");
        
      if (profile==null || profile.equalsIgnoreCase(Profile.DEV.name())){
       applicationContext.getEnvironment().setActiveProfiles(Profile.DEV.name());   
      }else if(profile.equalsIgnoreCase(Profile.PRODUCTION.name())){
       applicationContext.getEnvironment().setActiveProfiles(Profile.PRODUCTION.name()); 
      }else if(profile.equalsIgnoreCase(Profile.TEST.name())){
       applicationContext.getEnvironment().setActiveProfiles(Profile.TEST.name()); 
            }
     }
    }
    
  • Make sure the class is loaded during context loading
    in the project web.xml, insert the following:
                          
    <context-param>
         <param-name>contextInitializerClasses</param-name>
         <param-value>com.matomy.conf.ConfigurableApplicationContextInitializer</param-value>
    </context-param>

Phase 2: implementing the profile pattern 

This phase utilizes the infra we built before and implements the profile pattern.

Step 2.1 – create a properties interface
Create an interface for the configuration data you have.
In our case, the interface will provide access to the four configuration data items.
so it would look something like:

public interface SystemStrings {

String getJdbcUrl();
String getDBUsername();
String getDBPassword();
Boolean getHibernateShowSQL();
//..... 

Step 2.2 – create a class for each profile

Example for a development profile:

@Dev //Notice the dev annotation
@Component("systemStrings")
public class SystemStringsDevImpl extends AbstractSystemStrings implements SystemStrings{
      
 public SystemStringsDevImpl() throws IOException {
                //indication on the relevant properties file
  super("/properties/my_company_dev.properties");
 } 
}

Example for a production profile:

@Prouction //Notice the production annotation
@Component("systemStrings")
public class SystemStringsProductionImpl extends AbstractSystemStrings implements SystemStrings{
      
 public SystemStringsProductionImpl() throws IOException {
                //indication on the relevant properties file
  super("/properties/my_company_production.properties");
 } 
}

The two classes above are where the binding between the properties file and the related environment occur.

You’ve probably noticed that the classes extend an abstract class. This technique is useful so we won’t need to define each getter for each Profile, this would not be manageable in the long run, and really, there is no point of doing it.

The sweet and honey lies in the next step, where the abstract class is defined.

Step 2.3 – create an abstract file which holds the entire data

public abstract class AbstractSystemStrings implements SystemStrings{

 //Variables as in configuration properties file
private String jdbcUrl;
private String dBUsername;
private String dBPassword;
private boolean hibernateShowSQL;

public AbstractSystemStrings(String activePropertiesFile) throws IOException {
  //option to override project configuration from externalFile
  loadConfigurationFromExternalFile();//optional..
                //load relevant properties
  loadProjectConfigurationPerEnvironment(activePropertiesFile);  
 }

private void loadProjectConfigurationPerEnvironment(String activePropertiesFile) throws IOException {
  Resource[] resources = new ClassPathResource[ ]  {  new ClassPathResource( activePropertiesFile ) };
  Properties props = null;
  props = PropertiesLoaderUtils.loadProperties(resources[0]);
                jdbcUrl = props.getProperty("jdbc.url");
                dBUsername = props.getProperty("db.username"); 
                dBPassword = props.getProperty("db.password");
                hibernateShowSQL = new Boolean(props.getProperty("hibernate.show_sql"));  
}

//here should come the interface getters....

Phase 3: using the pattern

As you can recall, in previous steps we defined an interface for configuration data.

Now we will use the interface in a class which needs different data per environment.

Please note that this example is the key differentiator from the example given in the Spring blog, since now we don’t need to create a class for each profile, since in this case we use the same method across profiles and only the data changes.

Step 3.1 – example for using the pattern

@Configuration
@EnableTransactionManagement
//DB connection configuration class 
//(don't tell me you're still using xml... ;-)
public class PersistenceConfig {

 @Autowired
 private SystemStrings systemStrings; //Spring will wire by active profile

 @Bean
 public LocalContainerEntityManagerFactoryBean entityManagerFactoryNg(){
  LocalContainerEntityManagerFactoryBean factoryBean
  = new LocalContainerEntityManagerFactoryBean();
  factoryBean.setDataSource( dataSource() );
  factoryBean.setPersistenceUnitName("my_pu");       
  JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter(){
   {
    // JPA properties
    this.setDatabase( Database.MYSQL);
this.setDatabasePlatform("org.hibernate.dialect.MySQLDialect");
    this.setShowSql(systemStrings.getShowSqlMngHibernate());//is set per environemnt..           
   
   }
  };       
  factoryBean.setJpaVendorAdapter( vendorAdapter );
  factoryBean.setJpaProperties( additionalProperties() );

  return factoryBean;
 }
//...
@Bean
 public ComboPooledDataSource dataSource(){
  ComboPooledDataSource poolDataSource = new ComboPooledDataSource();
  try {
   poolDataSource.setDriverClass( systemStrings.getDriverClassNameMngHibernate() );
  } catch (PropertyVetoException e) {
   e.printStackTrace();
  }       
                 //is set per environemnt..
  poolDataSource.setJdbcUrl(systemStrings.getJdbcUrl());
  poolDataSource.setUser( systemStrings.getDBUsername() );
  poolDataSource.setPassword( systemStrings.getDBPassword() );
  //.. more properties...       
  return poolDataSource;
 }
}

I would appreciate comments and improvements.
Enjoy!

Reference: Spring Profile pattern from our JCG partner Gal Levinsky at the Gal Levinsky’s blog blog.

Do you want to know how to develop your skillset to become a Java Rockstar?

Subscribe to our newsletter to start Rocking right now!

To get you started we give you two of our best selling eBooks for FREE!

JPA Mini Book

Learn how to leverage the power of JPA in order to create robust and flexible Java applications. With this Mini Book, you will get introduced to JPA and smoothly transition to more advanced concepts.

JVM Troubleshooting Guide

The Java virtual machine is really the foundation of any Java EE platform. Learn how to master it with this advanced guide!

Given email address is already subscribed, thank you!
Oops. Something went wrong. Please try again later.
Please provide a valid email address.
Thank you, your sign-up request was successful! Please check your e-mail inbox.
Please complete the CAPTCHA.
Please fill in the required fields.

6 Responses to "Spring Profile pattern example"

  1. Philippe De Oliveira says:

    This all profile thing seems a very bad idea. Instead of coding it and put all versions (dev, prod, etc.) into the classpath, I prefer loading the same filename and deploy only one file into the classpath : the good one for each platform. They’ll all be in source control but there’s only one deployed along with a platform.
    A few reasons for that :
    – I don’t want my credentials (it’s an example) on production platform
    – In real life you usually don’t know production properties
    – I don’t want to filter some files when creating an artifact because it will generate differents artifacts; one per platform.

  2. Fvarg00 says:

    I enjoyed reading this. Cool stuff! Keep up the good work.

  3. sachin says:

    Hi
    can we put these properties file in our local machine instead of our project .
    So that we do not need to restart the server when we modify our property file.

    • Hi,

      It is highly NOT recommended to put the properties in the local machine.
      Doing so will make the configuration not sync with the source code (i.e. out of version control system).
      Always encourage the project to be self contained, with minimal dependency in system variables.

      I would recommend you to focus on version upgrade with minimal downtime (e.g. via load balancer which shift sessions from old version into the one).
      Another possible solution is to build a UI which enable you to temporary override certain parameters manually.

      Having said the above, you can define overriding the internal configuration via loading it from external file, simply extend the method loadProjectConfigurationPerEnvironment it in the AbstractSystemStrings class, and add a method for reading the values from a file (you can do it after the initial value definitions).

  4. Ramesh says:

    Any github link to the codebase

Leave a Reply


− 3 = five



Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use | Privacy Policy | Contact
All trademarks and registered trademarks appearing on Java Code Geeks are the property of their respective owners.
Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries.
Java Code Geeks is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.
Do you want to know how to develop your skillset and become a ...
Java Rockstar?

Subscribe to our newsletter to start Rocking right now!

To get you started we give you two of our best selling eBooks for FREE!

Get ready to Rock!
You can download the complementary eBooks using the links below:
Close