3

I need to integrate my spring boot app with the AWS Secret manager to retrieve the DB credentials from it. It has to work only on prod environment. This is what I did until now

   <dependency>
      <groupId>com.amazonaws</groupId>
      <artifactId>aws-java-sdk-secretsmanager</artifactId>
      <version>1.12.131</version>
    </dependency>

...

SpringApplication application = new SpringApplication(MyApplication.class);
    application.addListeners(new DatabasePropertiesListener());
    application.run(args);

And my listener implements the

ApplicationListener<ApplicationPreparedEvent>

Is there any way to run my method inside that listener only on specific environment ( kind of feature flag) ? I need to say that this is to early to use variables from properties files.

Jan
  • 31
  • 3

1 Answers1

0

Update:

When we have to decide before context creation (.i.e. ApplicationListener is no Component/Bean), then we (just mimic profiles) set:

# env: SET MY_FLAG=foo -> System.getenv("MY_FLAG")
# system: java -jar -Dmy.flag=foo myApp.jar -> System.getProperty("my.flag")
# cmd arg:
java -jar myApp.jar aws

..and issue it in our (spring boot) main method like:

if("aws".equalsIgnoreCase(args[0])) // alternatively: System.get...
  application.addListeners(new DatabasePropertiesListener());

Misunderstood answer

Sure we can: With spring (boot) core features!

Assuming our ApplicationListener is a "spring managed bean", we annotate it with:

@Component
@Profile("aws") // custom profile (name)
class MyApplicationListener implements ...
{ ... }

...this will not load this "bean" into our context, unless we define:

spring.profiles.acitve=aws,... # a comma separated list of profiles to activate

Profiles

Spring Profiles provide a way to segregate parts of your application configuration and make it be available only in certain environments. Any @Component (Or @Bean,@Service,@Repository...descendants), @Configuration or @ConfigurationProperties can be marked with @Profile to limit when it is loaded, as shown in the ...

... above example.

An advanced application of @Profile annotation:

  • with multiple profiles: "or"/"and" semantics
  • "not" (!) operator
@Profile("!local","aws") // (String[]: OR semantics) the annotated component/configuration/property will
// be loaded, when active profiles NOT contains "local" OR contains "aws"
// for AND semantics, we'd use (1 string): "!local & aws"

Activating Profile(s!)

spring.profiles.acitve can be set/added (like any spring property source) through several (14^^, precisely priorized) locations.

E.g. setting environment variable (5th lowest priority, but higher than application.properties (3rd lowest)):

SPRING_PROFILES_ACTIVE=aws

Or as a command line flag (when starting the application, 11th lowest/3rd highest priority):

java -jar myApp.jar --spring.profiles.active=aws,...
#comma separated list

For (spring) tests additionally exists an @ActiveProfiles annotation.

Remarks/Note

  • Deciding for profiles, we should ensure to "make it consistently" (not raising Nullpointer/BeaninitalizationExceptions ... with dependencies!;). If needed: Creating replacement/local/test (@Profile("!aws")) beans.

  • Activating a profile "xyz", automatically tries to load application-xyz.properties (with higher priority than application.properties (prio 3.1 - 3.4))...also in (spring-)cloud-config.

  • Not to forget: The default profile (activated by spring automatically, only when no explicit profile is activated).


Reference

For detailed documentation, please refer to:


Profile sample from Configuration Javadoc:

@Configuration classes may be marked with the @Profile annotation to indicate they should be processed only if a given profile or profiles are active:

@Profile("development")
@Configuration
public class EmbeddedDatabaseConfig {
     @Bean
     public DataSource dataSource() {
         // instantiate, configure and return embedded DataSource
     }
}
@Profile("production")
@Configuration
public class ProductionDatabaseConfig {
     @Bean
     public DataSource dataSource() {
         // instantiate, configure and return production DataSource
     }
}

Alternatively, you may also declare profile conditions at the @Bean method level — for example, for alternative bean variants within the same configuration class:

@Configuration
public class ProfileDatabaseConfig {

     @Bean("dataSource")
     @Profile("development")
     public DataSource embeddedDatabase() { ... }

     @Bean("dataSource")
     @Profile("production")
     public DataSource productionDatabase() { ... }
}

See the @Profile and Environment javadocs for further details.

xerx593
  • 12,237
  • 5
  • 33
  • 64
  • This is to late to load the secrets. It must be done before beans creation in the context. That's why I used the listener and added it with : application.addListeners(new DatabasePropertiesListener()); – Jan Dec 26 '21 at 00:30
  • 1
    See update! :) Merry X-mas! – xerx593 Dec 26 '21 at 01:02
  • I have managed to find two solutions. 1. Can read active profile or 2. environment variables to set the flag. I'm accessing ot by System.getenv – Jan Dec 26 '21 at 01:05