简体   繁体   中英

How to dynamically choose the configurations based on the environment in JAVA?

I have a utility class which has common configurations related to email-sender, the configurations changes based on the environment like Staging and Production . Now how can I dynamically choose the configurations based on the environment?

here is my code,

EmailUtility.java

package com.housecar.common;

public class EmailUtility {

 //For staging 
 public static final String FROM_EMAIL_ID = "xx12@xyz.com";
 public static final String FROM_NAME = "xyz";
 static final String SMTP_USERNAME = "xx12@xyz.com";
 static final String SMTP_PASSWORD = "15sss67$";
 public static final String REPLY_EMAIL_ID = "xx12@xyz.com";
 public static final String MAIL_SMTP_PORT = "587";
 public static final String MAIL_SMTP_SOCKET_FACTORY_PORT = "587";
 public static final String SMTP_HOST = "smtp.gmail.com";
 public static final String MAIL_SMTP_SOCKETFACTORY_CLASS = "javax.net.ssl.SSLSocketFactory";


 //for production

 /*public static final String FROM_EMAIL_ID = "admin@xyz.com";
 public static final String FROM_NAME = "xyz";
 static final String SMTP_USERNAME = "AKYeeeELEQAGAA"; // Replace with
                                                            // your SMTP
                                                            // username.
 static final String SMTP_PASSWORD = "gvwwwwwbgpGm/C/LmUYUK5HosQar7mTSwjl5MFeBRR";
 public static final String REPLY_EMAIL_ID = "admin@xyz.com";
 public static final String MAIL_SMTP_PORT = "587";
 public static final String MAIL_SMTP_SOCKET_FACTORY_PORT = "587";
 public static final String SMTP_HOST = "email-smtp.us-east-1.amazonaws.com";
 public static final String MAIL_SMTP_SOCKETFACTORY_CLASS = "javax.net.ssl.SSLSocketFactory";*/

}

In my code I'm manually commenting out the configuration!

You should lookup the system dependend values from a properties store (eg a properties file which is handled by Javas Properties class).

But you should not provide all systems information in the same file.

You should have separate files (with the same name) an have your deployment process copying the file for the target system to the expected location.

This way you enable operations team to manipulate (or preconfigue) a properties file with data you (as a developer) should not know, eg. passwords for high security systems...

Add the following into your application.properties file:

from.email.id = xx12@xyz.com
from.name = xyz

...

etc

You define those properties before you start your application (depending on your evn in your case). Of course you need to inject them into your class as follows:

@Value("${from.email.id}")
private String fromEmailId;

So now the fromEmailId will have the value given into the application.properties file.

Good luck.

EDIT:

If you want a little bit higher level of security you can use jasypt to cript your passwords into your applicaiton.properties file. At the end the password will look something like this:

smtp.password = ENC(5KZL2q+Ute21FzCsJy/h0aIp75TZgHBHx8L11R+jTJs0=)

You can use the properties file to solve your problem, like u can have two property files depending on the environment.

1) config_staging.properties 2) config_production.properties

and move your staging email configuration to config_staging.properties, move your production configuration to config_production.properties.

which will be configured while running the app.

for example,

config_staging.properties

 smtp.username = "xx12@xyz.com";
 smtp.password = "15sss67$";

config_production.properties

smtp.username = "AKYeeeELEQAGAA";
smtp.password = "gvwwwwwbgpGm/C/LmUYUK5HosQar7mTSwjl5MFeBRR";

then inject them into EmailUtility class,

@Value("${smtp.username}")
private String smtpUsername;


@Value("${smtp.password}")
private String smtpPassword;

In spring you can set an external configuration with properties or yaml .

Otherwise, if you need to change configuration while running the app and you're using a database, you could create a configuration table in your db with key-value configs and read them in your code.

For example, a table like this one:

CREATE TABLE configuration (
    key varchar(255),
    value varchar(255)
);

Where key is the property name and value is the property value.

A property

environment=development

could become

INSERT INTO configuration (key, value) VALUES('environment', 'development');

The other answers here are a step in the right direction.

Some suggest to move the configuration values into a .properties file. This is a big step in the right direction, but ignores the fact that you're using Spring Boot.

Some suggest to move the configuration values into the Spring application.properties or application.yaml files. This is the better choice for a Spring application. application.properties is actually just a .properties file like first suggested, but is automatically loaded by the Spring Framework. This is all described in Chapter 24. Externalized Configuration of the Spring Boot Reference Guide .

But, your question actually said:

[...] common configurations related to email-sender, the configurations changes based on the environment like Staging and Production.

Spring has a very nice feature, where you can have one set of configuration files (or even a single .yaml file), and the framework will automatically load the appropriate configuration values, depending on where the application is running, in Staging or Production. This is all described in Chapter 25. Profiles .

application-{profile}.properties or application-{profile}.yml allows you to customize your application based on spring profile.

It is possible to create beans only in {profile} using:

@Service
@Profile(profile-name)
public class ServiceImpl implements ServiceInterface {
}

Also it is possible to create beans based on properties values and conditions.

@Bean
@ConditionalOnProperty(prefix = EmailProperties.PREFIX, name = "fromId")
public BeanClass bean() {
    return new BeanClass(okHttpProperties.getFromId());
}

Conditional beans can be based on expression

@Bean
@ConditionalOnExpression("'${spring.datasource.driver-class-name}'=='com.mysql.jdbc.Driver'")
public DbUtil dbMysqlUtil() {
    ...
}

@Bean
@ConditionalOnExpression("'${spring.datasource.driver-class-name}'=='org.hsqldb.jdbc.JDBCDriver'")
public DbUtil dbHsqlUtil() {
    ...
}

Make configuration.properties file separate for stage and production. these configuration files will have corresponding properties. Now while Application startup you can first read Properties based on environment. environment value you can pass as argument in your application. You just need to write a simple ConfigurtionReader class which will read required configuration before application startup. Below is the code sample considering a very simple property file reading in plain java.

This will read a property file which is placed parallel to jar(current application).

public class ConfigurationReader {

    /** The job config prop. */
    private static final Properties JOB_CONFIG_PROP = new Properties();

    /**
     * Function is used to read configuration file that is placed in same place
     * where project jar is located.
     * 
     */
    public void readConfiguration(final String jobName) {
        File jarPath;
        String propertiesPath = " ";
        String jobConfigFile = " ";
        try {
            jarPath = new File(ConfigurationReader.class.getProtectionDomain().getCodeSource().getLocation().getPath());
            propertiesPath = jarPath.getParentFile().getAbsolutePath();
            jobConfigFile = propertiesPath + File.separator + AppConstants.CONFIGURATION_FOLDER_NAME
                    + File.separator + jobName + AppConstants.PROP;
            JOB_CONFIG_PROP.load(new FileInputStream(jobConfigFile));
        } catch (FileNotFoundException fileNtFoundExep) {
            throw fileNtFoundExep;
        } catch (IOException iOException) {
            throw iOException;
        } catch (Exception exception) {
            throw exception;
        }
    }

    public static String getPropertyValue(final String key) {
        return JOB_CONFIG_PROP.getProperty(key);
    }

    public static void setPropertyValue(final String key, final String value) {
        JOB_CONFIG_PROP.setProperty(key, value);
    }
}

You can then get/set the property value using...

ConfigurationReader.getPropertyValue("abc");
ConfigurationReader.setPropertyValue("abc","xyz");

This is just a simple approach to achieve the reading of properties from a property file. You can do something like this.

I have used both methods :

  1. specifying the active profile(eg. application-{profile}.properties) This is fine and pretty straight forward. The build process will resolve the environment specific properties file. Files for each env can be maintained in source control. Prod properties file(not accessible to dev/qe) can be secured in source control. The downside is that managing the different properties can get out of sync(aka configuration drift) (eg. a certain property key was added in dev but not in qe)

  2. specifying the custom argument in running the app (eg. -Dapp.home) You have to add code to customize the loading the of the properties file. Some customization can include handling of encrypted property values. Property key/values can be maintained in Chef (or equivalent tool). See sample code below:

     @Configuration public class MyExternalConfig { @Bean public static PropertyPlaceholderConfigurer configurer(){ //this is an external file path String appHomeConfig = System.getProperty("app.home") + File.separatorChar + "config"; PropertyPlaceholderConfigurer ppc = new PropertyPlaceholderConfigurer(); final Resource extResource = new FileSystemResource( new File( appHomeConfig, "application.properties") ); ppc.setLocations( new Resource[] { extResource } ); //add other properties file if needed ppc.setIgnoreUnresolvablePlaceholders( true ); return ppc; } } 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM