Spring boot and AWS Secret Manager - how to feature flaged the listener to set the secrets only on prod but not on test env - spring-boot

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.

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:
Spring Boot - Externalized Configuration
Spring Boot - Profiles
Spring Environment Abstraction chapter
#Profile Javadoc
#ActiveProfiles Javadoc
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.

Related

Creating a custom FactoryBean in Sprint Boot 2.3/Spring 5

I've got a spring-boot web application that's mostly working; my DataSource is properly configured by an external application.properties file.
Now I want to add properties to that file to help me instantiate and configure two instances of a class in my app. I have a APNsFactory that I currently instantiate manually and configure using JNDI, but I want to get away from JNDI calls:
#Bean
public
APNsFactory
apnsFactory()
throws
javax.naming.NamingException
{
sLogger.info("Configuring APNsFactory");
InitialContext ctx = new InitialContext();
APNsFactory f = new APNsFactory();
f.setProductionKeystorePath((String) ctx.lookup("java:comp/env/apns/prod/keystorePath"));
f.setProductionKeystorePassword((String) ctx.lookup("java:comp/env/apns/prod/keystorePassword"));
f.setDevelopmentKeystorePath((String) ctx.lookup("java:comp/env/apns/dev/keystorePath"));
f.setDevelopmentKeystorePassword((String) ctx.lookup("java:comp/env/apns/dev/keystorePassword"));
return f;
}
When running before in a standalone webapp container, Spring properly called that method and the JNDI context from the container’s <env-entry> tags was available.
I'm trying to update my APNsFactory to be a proper Spring FactoryBean<>, and I’ve given it a couple of #Autowire String variables that I want to be set by Spring Boot from the application.properties file.
For bonus points, I want this to be usable both in Spring Boot and in a standalone container like Tomcat or Resin.
For the life of me, I can't figure out how to get Spring to do this. There are dozens of examples for DataSources and other Beans already implemented by Spring, but none for a completely custom one, using application.properties, in a Spring Boot web environment.
I've seen some examples that use an XML config file, but I'm not sure how to do that with Spring Boot.
I don't think you need a factory bean here.
You already have spring boot that can read application.properties out-of-the-box:
So try the following:
Create key/values in the application.properties file:
myapp.keystore.path=...
myapp.keystore.passwd=...
// the same for other properties
Create ConfigurationProperties class
#ConfigurationProperties(prefix="myapp.keystore")
public class MyAppKeyStoreConfigProperties {
private String path; // the names must match to those defined in the properties file
private String passwd;
... getters, setters
}
In the class marked with #Configuration (the one where you create #Bean public APNsFactory apnsFactory()) do the following:
#Configuration
// Note the following annotation:
#EnableConfigurationProperties(MyAppKeyStoreConfigProperties.class)
public class MyConfiguration {
// Note the injected configuration parameter
#Bean public APNsFactory apnsFactory(MyAppKeyStoreConfigProperties config) {
APNsFactory f = new APNsFactory();
f.setProductionKeystorePath(config.getKeyPath());
and so on
}
}
I've intentionally didn't show the separation between production/dev stuff.
In spring boot you have profiles so that the same artifact (WAR, JAR whatever) can be configured to run with different profile and depending on that the corresponding properties will be read.
Example:
If you're running with prod profile, then in addition to application.properties that will be loaded anyway, you can put these keystore related definitions to application-prod.properties (the suffix matches the profile name) - spring boot will load those automatically. The same goes for dev profile of course.
Now I haven't totally understand the "bonus points" task :) This mechanism is spring boot proprietary way of dealing with configuration. In "standalone" server it should still have a WAR with spring boot inside so it will use this mechanism anyway. Maybe you can clarify more, so that I / our colleagues could provide a better answer

Split jackson configuration into separate properties

I'm using Spring Boot 2.2.5.RELEASE and would like to split my application.properties into separate files. There are already similar questions on StackOverflow but none of them seem to work for configuring Jackson.
My current non working solution is the following:
root/
- application.properties (without Jackson configuration)
- jackson-configuration.properties (includes Jackson configuration)
Jackson configuration class:
#Configuration
#PropertySource("/jackson-configuration.properties")
public class JacksonConfiguration {
}
Please note, I've tried different ways to specify the path including:
"/jackson-configuration.properties"
"jackson-configuration.properties"
"classpath:/jackson-configuration.properties"
"classpath:jackson-configuration.properties"
Spring Boot does not seem to use the configuration. If I copy it over into the application.properties - it works.
Content of jackson-configuration.properties:
spring.jackson.property-naming-strategy=SNAKE_CASE
spring.jackson.mapper.sort-properties-alphabetically=true
spring.jackson.deserialization.fail-on-unknown-properties=true
spring.jackson.parser.strict-duplicate-detection=true
spring.jackson.time-zone=Europe/Zurich
My application is annotated with #SpringBootApplication , so it should scan for additional properties.
/edit
I just realized the problem is the testing, not the productive code itself. If I start the application it works. What doess not work is testing with #JsonTest. I can fix this problem by adding the following line to my tests #ContextConfiguration(classes = {JacksonConfiguration.class}). But in turn, this causes the annotation #JsonComponent to stop working but only for the #JsonTest annotated classes.
See the documentation here. Here is an excerpt from the documentation
In order to resolve ${...} placeholders in definitions or
#Value annotations using properties from a PropertySource, you must
ensure that an appropriate embedded value resolver is registered in
the BeanFactory used by the ApplicationContext. This happens
automatically when using in XML. When
using #Configuration classes this can be achieved by explicitly
registering a PropertySourcesPlaceholderConfigurer via a static #Bean
method.
You need to create a bean like this
#Bean
public static PropertySourcesPlaceholderConfigurer devPropertyPlaceholderConfigurer() throws IOException {
PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer();
configurer.setLocations(new PathMatchingResourcePatternResolver().getResources("file:pathtToFile"));
configurer.setIgnoreUnresolvablePlaceholders(true);
return configurer;
}

How to correctly using Spring profile on windows

I am trying to maintain different Spring profiles for development and production, for which I have created a folder(web skeleton) on my desktop with my Spring Boot project, application-dev.properties and application-prod.properties.
However, I am unable to import the profile into my project. The code that I use to import it to my project is as follows.
#Configuration
#Profile("dev")
#PropertySource("file:///${user.home}/web skeleton/application-dev.properties")
public class DevelopmentConfig {
#Bean
public EmailService emailService(){
return new MockEmailService();
}
Can someone tell me if this is the right way to use PropertySource in Spring.
You can optionally define a custom source where we’re storing these properties, else the default location (classpath:application.properties) is looked up. So we now add the above annotations to the existing properties class:
#Configuration
#PropertySource("classpath:configprops.properties")
#ConfigurationProperties(prefix = "dev")
public class ConfigProperties {
// previous code
}
Now any properties defined in the property file that has the prefix dev and the same name as one of the properties are automatically assigned to this object.
#Simple properties
dev.host=mailer#mail.com
dev.port=9000
Check this
I have done this kind of configuration too
Just add below code in your configuration class
#PropertySource("classpath:application-${spring.profiles.active}.properties")
And this propery in application.properties
spring.profiles.active=dev
you can change it to prod and cert as per you need.

Common and profile specific properties in a single application.properties file

I am writing a spring mvc application. I want to make use of spring profiles' features. I want to have a single application.properties with common and profile specific properties. I also want to have a set a default profile in the same file. How do i do this?
You could create an extra spring configuration class to dynamically return the properties based on the spring profile:
#Configuration
#PropertySource("classpath:application.properties")
public class QuotePropertyConfiguration {
#Inject
private Environment environment;
#Bean
public String getMySpecificProperty() {
return environment.getProperty("myprop." + Arrays.toString(env.getActiveProfiles()) + ".thing");
}
}
That will return you e.g. myprop.dev.thing, myprop.test.thing, myprop.prod.thing etc. depending on the active spring profiles.
Alternatively you can also do this with multiple application.properties files: From the spring documentation:
In addition to application.properties files, profile-specific
properties can also be defined using the naming convention
application-{profile}.properties. The Environment has a set of default
profiles (by default [default]) which are used if no active profiles
are set (i.e. if no profiles are explicitly activated then properties
from application-default.properties are loaded).

How to dynamically register the bean specific properties on bean initialization

I want to register the bean specific properties on bean creation if not exists in the application. For example
public class BeanOne {
#Value("${prop1}")
private String property1;
}
public class BeanTwo {
#Value("${prop2}")
private String property2;
}
I want to register the property 'prop1' and 'prop2' to instantiate the above two beans successfully.
My application is configured with spring-cloud-consul-config. By default consul is not configured with these properties in that time, application will automatically register these properties into consul through ecwid/consul-api
Currently the application throws "IllegalArgumentException" with message "Could not resolve placeholder 'prop1' in string value "${prop1}"
Any Suggestions to avoid the above error.
There are various ways you can do this.
My preferred approach is to set the properties in one or more files externally and then use the #PropertySource("file:///full-path-to-property-file") annotation in a #Configuration class.
This approach, combined with Spring Profiles is very powerful.
Example if you are not using profiles:
Create an application.properties file somewhere on your PC. A good
place to put this is ${user.home}/.somefolder/application.properties
Add the values for prop1 and prop2
Change the permissions on that folder and file. For example you can
run chmod u on .somefolder and chmod 600 on the properties file
Create a Spring Java Configuration class and annotate it with
#Configuration. For example you can create an ApplicationConfig
class in a sub package of your Spring Boot application and annotate
it with #Configuration
Add the #PropertySource annotations as follows:
#PropertySource("file:///${user.home}/.somefolder/application.properties")
An example from my course on Spring Boot, AWS, Bootstrap and Stripe:
#Configuration
#EnableJpaRepositories(basePackages = "com.devopsbuddy.backend.persistence.repositories")
#EntityScan(basePackages = "com.devopsbuddy.backend.persistence.domain.backend")
#EnableTransactionManagement
#PropertySource("file:///${user.home}/.devopsbuddy/application-common.properties")
public class ApplicationConfig {
}
That's it!

Resources