Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamically switching between properties files based on request header parameter in spring boot application

I am developing an multi-tenant REST spring boot application. I am able to dynamically switch between different data-sources based on a header value in every request. But my problem is on the application.properties file. The different tenants have different values for the same properties in their properties files.

How can I separate the properties files per tenant and also dynamically determine which properties files to use based on a value in the request header

like image 535
bruce_wayne Avatar asked Sep 28 '19 14:09

bruce_wayne


People also ask

How do you update a properties file dynamically in Java?

Instantiate the Properties class. Populate the created Properties object using the put() method. Instantiate the FileOutputStream class by passing the path to store the file, as a parameter.

How do you change file location in application properties spring boot?

properties in default location. Spring Boot loads the application. properties file automatically from the project classpath. All you have to do is to create a new file under the src/main/resources directory.


1 Answers

You can't switch profiles at runtime. Your options are limited to either creating a new ApplicationContext which comes with its own drawbacks or you can load the tenant property files at startup and implement a tenant-specific getProperty method to call when needed.

This ought-to handle the latter case:

@Component
public class TenantProperties {

  private Map<String, ConfigurableEnvironment> customEnvs;

  @Inject
  public TenantProperties(@Autowired ConfigurableEnvironment defaultEnv,
      @Value("${my.tenant.names}") List<String> tenantNames) {

    this.customEnvs = tenantNames
        .stream()
        .collect(Collectors.toMap(
            Function.identity(),
            tenantId -> {
              ConfigurableEnvironment customEnv = new StandardEnvironment();
              customEnv.merge(defaultEnv);
              Resource resource = new ClassPathResource(tenantId + ".properties");

              try {
                Properties props = PropertiesLoaderUtils.loadProperties(resource);
                customEnv.getPropertySources()
                    .addLast(new PropertiesPropertySource(tenantId, props));
                return customEnv;
              } catch (IOException ex) {
                throw new RuntimeException(ex);
              }
            }));
  }

  public String getProperty(String tenantId, String propertyName) {

    ConfigurableEnvironment ce = this.customEnvs.get(tenantId);
    if (ce == null) {
      throw new IllegalArgumentException("Invalid tenant");
    }

    return ce.getProperty(propertyName);
  }
}

You need to add a my.tenant.names property to your main application properties that contains a comma separated list of tenant names (name1, name2, etc.). tenant-specific properties are loaded from name1.properties, ... from the classpath. You get the idea.

like image 128
Andy Brown Avatar answered Oct 30 '22 20:10

Andy Brown