How to active profile as environment variable - spring

I was configuring environment variables using spring profiles in my spring boot application. There i did configuration like
My interface is
public interface EnvConfiguration {
String getServerUrl();
}
My development configuration is
#Component
public class DevelopmentConfig implements EnvConfiguration{
#Value("${DEV}")
private String serverUrl;
#Override
public String getServerUrl(){
return serverUrl;
}
}
#Configuration
#Profile("dev")
public class DevelopmentProfile {
#Bean
public EnvConfiguration getDevelopmentConfig(){
return new DevelopmentConfig();
}
}
Same as i did configured for production environment
#Component
public class ProductionConfig implements EnvConfiguration {
#Value("${PROD}")
private String serverUrl;
#Override
public String getServerUrl(){
return serverUrl;
}
}
#Configuration
#Profile("prod")
public class ProductionProfile {
#Bean
public EnvConfiguration getProductionConfig(){
return new ProductionConfig();
}
}
Now i configured environment variable in eclipse using run configurations->agruments
-Dspring.profiles.active="dev"
Now when i trying to run my application,i am getting error:
expected single matching bean but found 2: productionConfig,developmentConfig
So please help me what am i missing there ?
Thanks in advance!

I was adding programming arguments,we have to add vm arguments

Why are you trying to configure environment properties with Java ?
You could put all your configuration into an application.properties.
Then if you want dev environment, you juste override the properties you want in application-dev.properties.
The same for prod in application-prod.properties.
Then you start as you did with -Dspring.profiles.active=dev and you will be able to retrieve value with #Value.

Related

Can we use #Environment to access a properties at two different places

I 'm trying to access the properties file other than a place where all the application's configuration resides(Datasource,SessionFactory,TransactionManager), hence I'm trying to use #Environment twice in my application.
I'm trying to load a properties file to implement a email notification functionality to access the properties file content using env by auto wiring. But I'm failing while reading the property file using env.getProperty("email.smtp.host")); Getting null in the place of property value when executing the app.
Actually, I'm trying to use two Configuration file, is this what blocks me to autowire Environment property ? I tried to replace with #Component and adding email package to componentscan list as well. I'm thinking that, is Environement property applicable only to #Configuration file ? Can someone help me to debug the issue please or what I'm missing here to initiate the Environment ?
I tried to resolve this by implementing EnvironmentAware interface as below to get the property value using load() but that still didn't help me.
Tried to use PropertySourcesPlaceholderConfigurer as below.
#Bean
public static PropertySourcesPlaceholderConfigurer propertyPlaceHolderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
#Autowired
private Environment env;
#Configuration
#PropertySource("classpath:email.properties")
public class PropertiesUtil implements EnvironmentAware {
#Autowired
private Environment environment;
public void sendPlainTextEmail(String mailMessage) throws AddressException, MessagingException{
try {
// Configure SMTP
Properties properties = new Properties();
properties.setProperty("mail.smtp.host",this.env.getProperty("email.smtp.host"));
properties.setProperty("mail.smtp.port", "25");
}
#Override
public void setEnvironment(final Environment environment) {
this.environment = environment;
}
#Bean
public String load(String propertyName)
{
return environment.getRequiredProperty(propertyName);
}
}
Expecting to read the email property but getting only Null and no other information to debug.

How to inject java.nio.file.Path dependency using #ConfigurationProperties

I'm using Spring Boot and have the following Component class:
#Component
#ConfigurationProperties(prefix="file")
public class FileManager {
private Path localDirectory;
public void setLocalDirectory(File localDirectory) {
this.localDirectory = localDirectory.toPath();
}
...
}
And the following yaml properties file:
file:
localDirectory: /var/data/test
I would like to remove the reference of java.io.File (of setLocalDirectory) by replacing with java.nio.file.Path. However, I receive a binding error when I do this. Is there way to bind the property to a Path (e.g. by using annotations)?
To add to jst's answer, the Spring Boot annotation #ConfigurationPropertiesBinding can be used for Spring Boot to recognize the converter for property binding, as mentioned in the documentation under Properties Conversion:
#Component
#ConfigurationPropertiesBinding
public class StringToPathConverter implements Converter<String, Path> {
#Override
public Path convert(String pathAsString) {
return Paths.get(pathAsString);
}
}
I don't know if there is a way with annotations, but you could add a Converter to your app. Marking it as a #Component with #ComponentScan enabled works, but you may have to play around with getting it properly registered with the ConversionService otherwise.
#Component
public class PathConverter implements Converter<String,Path>{
#Override
public Path convert(String path) {
return Paths.get(path);
}
When Spring sees you want a Path but it has a String (from your application.properties), it will lookup in its registry and find it knows how to do it.
I took up james idea and defined the converter within the spring boot configuration:
#SpringBootConfiguration
public class Configuration {
public class PathConverter implements Converter<String, Path> {
#Override
public Path convert(String path) {
return Paths.get(path);
}
}
#Bean
#ConfigurationPropertiesBinding
public PathConverter getStringToPathConverter() {
return new PathConverter();
}
}

Properties in Spring Environment

I'm trying to create a custom property placeholder.
At my Spring Configuration Class I have this:
#Configuration
#Import(ConfigurationClass2.class)
public class ConfigurationClass1 {
#Bean
#Lazy(false)
public static PropertySourcesPlaceholderConfigurer settings() throws IOException {
// custom load of my properties file
// legacy configuration, comes from a SVN repository
// I need to download it and then do some logic and finally
// load it.
}
}
At another configuration class I want to access some properties using the Environment, like this:
#Configuration
#EnableScheduling
public class ConfigurationClass2 {
#Autowired
private Environment env;
#Bean
public DataSource dataSource() {
String jdbcUrl = env.getProperty('jdbc.url');
.... // jdbcUrl is null!!!!
}
#Scheduled(cron = "${cronExpression}")
public void worker() {
}
}
I don't want to use #Value or #PropertySource.
I need to load this properties manually and access this values programmatically!
How can I solve this?

Spring 4, MongoDB, using #Value in MongoConfiguration

I use a MongoConfiguration class to setup my Sping 4 MongoDB. I want to read properties from application.properties so I use #Value:
....
#Configuration
#EnableMongoRepositories
#ComponentScan(basePackageClasses = {Application.class})
public class MongoConfiguration extends AbstractMongoConfiguration {
#Value("${mongodb.host}")
String mongodb_host;
#Value("${mongodb.port}")
int mongodb_port;
#Value("${mongodb.databasename}")
String mongodb_databasename;
#Override
protected String getDatabaseName() {
return mongodb_databasename;
}
#Override
public Mongo mongo() throws Exception {
return new MongoClient( mongodb_host, mongodb_port );
}
#Override
protected String getMappingBasePackage() {
return "com.example.mongodb01";
}
}
This works fine for a web application -- but when I try the same idea in a command line Java application it fails (it's as if the application.properties was found but #Value never ran). I know I am reading the applications.properties file OK. It must have something to do with the differences in running in a servlet container vs. an application but after much searching and trials I have not been able to resolve this and fix it. I would appreciate any help on this -- Thank you!
I did see a similar question and I tried adding the below to my MongoConfiguration but still had the same problem:
#Bean
public static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
Did you include the #PropertySource
#Configuration
#PropertySource("classpath:application.properties")
#EnableMongoRepositories
#ComponentScan(basePackageClasses = {Application.class})
public class MongoConfiguration extends AbstractMongoConfiguration {
...

Can #PropertySources be chosen by Spring profile?

I have a Spring 3.1 #Configuration that needs a property foo to build a bean. The property is defined in defaults.properties but may be overridden by the property in overrides.properties if the application has an active override Spring profile.
Without the override, the code would look like this, and work...
#Configuration
#PropertySource("classpath:defaults.properties")
public class MyConfiguration {
#Autowired
private Environment environment;
#Bean
public Bean bean() {
...
// this.environment.getRequiredProperty("foo");
...
}
}
I would like a #PropertySource for classpath:overrides.properties contingent on #Profile("overrides"). Does anyone have any ideas on how this could be achieved? Some options I've considered are a duplicate #Configuration, but that would violate DRY, or programmatic manipulation of the ConfigurableEnvironment, but I'm not sure where the environment.getPropertySources.addFirst() call would go.
Placing the following in an XML configuration works if I inject the property directly with #Value, but not when I use Environment and the getRequiredProperty() method.
<context:property-placeholder ignore-unresolvable="true" location="classpath:defaults.properties"/>
<beans profile="overrides">
<context:property-placeholder ignore-unresolvable="true" order="0"
location="classpath:overrides.properties"/>
</beans>
Update
If you're trying to do this now, check out Spring Boot's YAML support, particularly the 'Using YAML instead of Properties' section. The profile support there would make this question moot, but there isn't #PropertySource support yet.
Add the overriding #PropertySource in a static inner class. Unfortunately, you must specify all property sources together which means creating a "default" profile as the alternative to "override".
#Configuration
public class MyConfiguration
{
#Configuration
#Profile("default")
#PropertySource("classpath:defaults.properties")
static class Defaults
{ }
#Configuration
#Profile("override")
#PropertySource({"classpath:defaults.properties", "classpath:overrides.properties"})
static class Overrides
{
// nothing needed here if you are only overriding property values
}
#Autowired
private Environment environment;
#Bean
public Bean bean() {
...
// this.environment.getRequiredProperty("foo");
...
}
}
I suggest, defining two files, where the second is optional with the profile as suffix:
#Configuration
#PropertySources({
#PropertySource("classpath:/myconfig.properties"),
#PropertySource(value = "classpath:/myconfig-${spring.profiles.active}.properties", ignoreResourceNotFound = true)
})
public class MyConfigurationFile {
#Value("${my.prop1}")
private String prop1;
#Value("${my.prop2}")
private String prop2;
}
You can do:
<context:property-placeholder location="classpath:${spring.profiles.active}.properties" />
Edit: if you need something more advanced, you can register your PropertySources on application startup.
web.xml
<context-param>
<param-name>contextInitializerClasses</param-name>
<param-value>com.xxx.core.spring.properties.PropertySourcesApplicationContextInitializer</param-value>
</context-param>
file you create:
public class PropertySourcesApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
private static final Logger LOGGER = LoggerFactory.getLogger(PropertySourcesApplicationContextInitializer.class);
#Override
public void initialize(ConfigurableApplicationContext applicationContext) {
LOGGER.info("Adding some additional property sources");
String[] profiles = applicationContext.getEnvironment().getActiveProfiles()
// ... Add property sources according to selected spring profile
// (note there already are some property sources registered, system properties etc)
applicationContext.getEnvironment().getPropertySources().addLast(myPropertySource);
}
}
Once you've done it you just need to add in your context:
<context:property-placeholder/>
I can't really answer to your question about multiple profiles but I guess you activate them on such an initializer, and you could register the appropriate PropertySource items during profile activations.
I can't think of any other way than one you have suggested Emerson, which is to define this bean in a separate #Configuration file with an #Profile annotation:
#Configuration
#Profile("override")
#PropertySource("classpath:override.properties")
public class OverriddenConfig {
#Autowired
private Environment environment;
#Bean
public Bean bean() {
//if..
}
}
In case you need to support multiple profiles you could do something like this:
#Configuration
public class Config {
#Configuration
#Profile("default")
#PropertySource("classpath:application.properties")
static class DefaultProperties {
}
#Configuration
#Profile("!default")
#PropertySource({"classpath:application.properties", "classpath:application-${spring.profiles.active}.properties"})
static class NonDefaultProperties {
}
}
That way you don't need to define a static configuration class for each profile.
Thanks David Harkness for putting me into the right direction.
Note: This answer provides an alternate solution to using properties files with #PropertySource. I went this route because it was too cumbersome trying to work with multiple properties files that may each have overrides while avoiding repetitive code.
Create a POJO interface for each related set of properties to define their names and types.
public interface DataSourceProperties
{
String driverClassName();
String url();
String user();
String password();
}
Implement to return the default values.
public class DefaultDataSourceProperties implements DataSourceProperties
{
public String driverClassName() { return "com.mysql.jdbc.Driver"; }
...
}
Subclass for each profile (e.g. development, production) and override any values that differ from the default. This requires a set of mutually-exclusive profiles, but you can easily add "default" as the alternative to "overrides".
#Profile("production")
#Configuration
public class ProductionDataSourceProperties extends DefaultDataSourceProperties
{
// nothing to override as defaults are for production
}
#Profile("development")
#Configuration
public class DevelopmentDataSourceProperties extends DefaultDataSourceProperties
{
public String user() { return "dev"; }
public String password() { return "dev"; }
}
Finally, autowire the properties configurations into the other configurations that need them. The advantage here is that you don't repeat any #Bean creation code.
#Configuration
public class DataSourceConfig
{
#Autowired
private DataSourceProperties properties;
#Bean
public DataSource dataSource() {
BoneCPDataSource source = new BoneCPDataSource();
source.setJdbcUrl(properties.url());
...
return source;
}
}
I am still not convinced I'll stick with this over manually configuring properties files based on the active profiles in a servlet context initializer. My thought was that doing manual configuration would not be as amenable to unit testing, but I'm not so sure now. I really prefer reading properties files to a list of property accessors.
All mentioned here solutions are a bit awkward, work only with one profile preset, and they won't work with more/other profiles. Currently a Spring team refuses to introduce this feature. But here's the working workaround I've found:
package com.example;
public class MyPropertySourceFactory implements PropertySourceFactory, SpringApplicationRunListener {
public static final Logger logger = LoggerFactory.getLogger(MyPropertySourceFactory.class);
#NonNull private static String[] activeProfiles = new String[0];
// this constructor is used for PropertySourceFactory
public MyPropertySourceFactory() {
}
// this constructor is used for SpringApplicationRunListener
public MyPropertySourceFactory(SpringApplication app, String[] params) {
}
#Override
public void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) {
activeProfiles = environment.getActiveProfiles();
}
#Override
public PropertySource<?> createPropertySource(String name, EncodedResource encodedResource) throws IOException {
logger.info("Loading: {} with profiles: {}", encodedResource.toString(), activeProfiles);
// here you know all profiles and have the source Resource with main
// properties, just try to load other resoures in the same path with different
// profile names and return them as a CompositePropertySource
}
}
To make it working you have to have src/main/resources/META-INF/spring.factories with the following content:
org.springframework.boot.SpringApplicationRunListener=com.example.MyPropertySourceFactory
Now you can put your custom properties file somewhere and load it with #PropertySources:
#Configuration
#PropertySource(value = "classpath:lib.yml", factory = MyPropertySourceFactory.class)
public class PropertyLoader {
}

Resources