i have properties like this
connection.local=0.0.0.0
i write it on application.properties and at application-local.properties it is correct or not ?
but when i want to get this value with annotation
i use plain java for build apps. and use spring context for get value and annotations
#Component
#Scope("singleton")
#Slf4j
#Configuration
public class SocketEngine extends Thread {
/**
* This is to make sure that the server is running and trying even when
* idxdatafeed disconnects
*/
#Value("${connection.local}")
private String connectionLocalhost;
public void run() {
while (true) {
Socket server = null;
String firstData="xvabv";
try {
log.info("Connecting to server " + connectionLocalhost+"!");
server = new Socket(connectionLocalhost, 9010);
server.setSoTimeout(10000);
PrintWriter writer= new PrintWriter(server.getOutputStream());
i got value of connectionLocal is null why like that ?
You may solve your problem by injecting the value in the constructor:
public SocketEngine(#Value("${connection.local}") String connectionLocalhost) {
this.connectionLocalhost = connectionLocalhost;
run();
}
This should resolve the problem. But really I don't why spring here acts like that (tested). Also you don't need the annotation #Compnent because #Configuration includes it.
The official approach is to define the following method in your configuration class:
#Bean
public static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
But it didn't work, and that's why I'm confused!
I solved the problem , add like below code.
#PropertySource(value = "classpath:application.properties", ignoreResourceNotFound = true)
Related
I have defined some external properties in my main class as follows:
#PropertySources({
#PropertySource(value = "classpath:application.properties", ignoreResourceNotFound = true),
#PropertySource(value = "file:/opt/app/conf/database.properties", ignoreResourceNotFound = true),
#PropertySource(value = "file:/opt/app/conf/overrides.properties", ignoreResourceNotFound = true)
})
For some of the properties I would like to do some post processing, for example encrypting or enrichment before they are actually used by any beans. One such property is spring.datasource.password
I have done the following:
Write an ApplicationContextInitializer<ConfigurableApplicationContext> and tried to process those properties in the initialize() method
public class MyApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
#Override
public void initialize(ConfigurableApplicationContext applicationContext) {
ConfigurableEnvironment environment = applicationContext.getEnvironment();
String value = environment.getProperty("spring.datasource.password");
System.out.println("The value in initializer is " + value);
System.out.println("The database url in initializer is " + environment.getProperty("spring.datasource.url"));
System.out.println("The database username in initializer is " + environment.getProperty("spring.datasource.username"));
// .. other code
}
}
and included the above class in the META-INF/spring.factories as follows:
org.springframework.context.ApplicationContextInitializer=com.myapp.MyApplicationContextInitializer
I am seeing null values in all of the above properties that are printed though both database.properties and overrides.properties are present. They are the very first statements to be printed (even before the banner)
Another approach I tried is org.springframework.boot.env.EnvironmentPostProcessor and adding in META-INF/spring.factories as
org.springframework.boot.env.EnvironmentPostProcessor=com.myapp.PropertiesProcessor
But still, I get the same null value.
Interestingly, when I pass in the
-Dspring.config.location="file:/opt/app/conf/database.properties, file:/opt/app/conf/overrides.properties"
system property before executing the war, it works i.e. the values are printed.
But I do not want to manually pass in the system property at runtime. Is there a way to do it?
What is wrong with my code or approach. Are there any other ways of doing the post-processing before actually creating the beans?
I solved similar problem by adding property source with highest precedence.
Add META-INF/spring.factories with content:
org.springframework.boot.env.EnvironmentPostProcessor=
com.example.SettingsPropertiesAddingPostProcessor
SettingsPropertiesAddingPostProcessor implementation:
public class SettingsPropertiesAddingPostProcessor implements EnvironmentPostProcessor {
private static final String SETTINGS_CONFIG_PATH = "/tmp/settings.properties";
#Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
File settingsFile = new File(SETTINGS_CONFIG_PATH);
if (!settingsFile.exists()) {
log.debug("Config file not found, skipping adding custom property source");
return;
}
log.debug("Config file found, adding custom property source");
Properties props = loadProperties(settingsFile);
MutablePropertySources propertySources = environment.getPropertySources();
propertySources.addFirst(new PropertiesPropertySource("settings-source", props));
}
private Properties loadProperties(File f) {
FileSystemResource resource = new FileSystemResource(f);
try {
return PropertiesLoaderUtils.loadProperties(resource);
} catch (IOException ex) {
throw new IllegalStateException("Failed to load local settings from " + f.getAbsolutePath(), ex);
}
}
}
That should be all.
Following the advice of #M. Deinum, regarding using "spring.config.additional-location", I have made a workaround as follows:
#SpringBootApplication
#EnableSwagger2
public class MyApp extends SpringBootServletInitializer {
public static void main(String[] args) {
System.setProperty("spring.config.additional-location", "file:/opt/app/conf/database.properties, file:/opt/app/conf/overrides.properties");
SpringApplication springApplication = new SpringApplication(MyApp.class);
springApplication.run(args);
}
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
System.out.println("Setting properties onStartup");
System.setProperty("spring.config.additional-location", "file:/opt/app/conf/database.properties, file:/opt/app/conf/overrides.properties");
super.onStartup(servletContext);
}
}
I have called the System.setProperty() in the onStartup() method of the SpringBootServletInitializer by overriding it as above and then invoked the super class' onStartup()
The latter part i.e. setting system property in onStartup method helps when the application is deployed in a web container like Tomcat.
Note: We can also append the properties to spring.config.additional-location instead of set so that other additional locations can also be added during runtime.
I am trying configuring multiple couchbase data source using springboot-data-couchbase.
This is a way I tried to attach two couchbase sources with 2 repositories.
#Configuration
#EnableCouchbaseRepositories("com.xyz.abc")
public class AbcDatasource extends AbstractCouchbaseConfiguration {
#Override
protected List<String> getBootstrapHosts() {
return Collections.singletonList("ip_address_of_couchbase");
}
//bucket_name
#Override
protected String getBucketName() {
return "bucket_name";
}
//password
#Override
protected String getBucketPassword() {
return "user_password";
}
#Override
#Bean(destroyMethod = "disconnect", name = "COUCHBASE_CLUSTER_2")
public Cluster couchbaseCluster() throws Exception {
return CouchbaseCluster.create(couchbaseEnvironment(), "ip_address_of_couchbase");
}
#Bean( name = "BUCKET2")
public Bucket bucket2() throws Exception {
return this.couchbaseCluster().openBucket("bucket2", "somepassword");
}
#Bean( name = "BUCKET2_TEMPLATE")
public CouchbaseTemplate newTemplateForBucket2() throws Exception {
CouchbaseTemplate template = new CouchbaseTemplate(
couchbaseClusterInfo(), //reuse the default bean
bucket2(), //the bucket is non-default
mappingCouchbaseConverter(), translationService()
);
template.setDefaultConsistency(getDefaultConsistency());
return template;
}
#Override
public void configureRepositoryOperationsMapping(RepositoryOperationsMapping baseMapping) {
baseMapping
.mapEntity(SomeDAOUsedInSomeRepository.class, newTemplateForBucket2());
}
}
similarly:
#Configuration
#EnableCouchbaseRepositories("com.xyz.mln")
public class MlnDatasource extends AbstractCouchbaseConfiguration {...}
Now the problem is there is no straight forward way to specify namespace based datasource by attaching different beans to these configurations like in springdata-jpa as springdata-jpa support this feature do using entity-manager-factory-ref and transaction-manager-ref.
Due to which only one configuration is being picked whoever comes first.
Any suggestion is greatly appreciated.
Related question: Use Spring Data Couchbase to connect to different Couchbase clusters
#anshul you are almost there.
Make one of the Data Source as #primary which will be used as by default bucket.
Wherever you want to use the other bucket .Just use specific bean in your service class with the qualifier below is the example:
#Qualifier(value = "BUCKET1_TEMPLATE")
#Autowired
CouchbaseTemplate couchbaseTemplate;
Now you can use this template to perform all couch related operations on the desired bucket.
I'm using Spring Boot and I have a properties file p.properties:
p1 = some val1
p2 = some val2
Configuration class:
#Configuration
#PropertySource("classpath:p.properties")
public class myProperties {
public myProperties () {
super();
}
#Bean
public static PropertySourcesPlaceholderConfigurer placeholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
And I'm using this in order to access the property:
#Value("${p1}")
private String mProperty;
Everything works great.
I want to change p1 in p.properties file from outside of the app and the next time that I'll use mProperty, it will contains the new value without restarting the app.
Is it possible?
Thanks,
Avi
You can simply use spring boot actuator.
Just add the actuator dependency in your maven/gradle config and you should be seeing live reloads when you update the property file.
Note: You won't have to restart the app but actuator will do live reloads on its own.
If you want to change the properties at runtime and don't want to restart the server then follow the below steps:
Application.properties
app.name= xyz
management.endpoints.web.exposure.include=*
Add below dependencies in pom.xml
org.springframework.boot
spring-boot-starter-actuator
org.springframework.cloud
spring-cloud-context
2.0.1.RELEASE
3)Place application.properties in config folder . The config folder must be in the location from where you will run the jar.
Add ApplcationProperties.java
#RefreshScope
#Component
#ConfigurationProperties(prefix = "app")
public class ApplcationProperties {
private String name;
//getter and setter
}
Write ApplicationController.java and inject ApplcationProperties
#Autowired
ApplcationProperties applcationProperties;
#RestController
public Class ApplicationController
{
#GetMapping("/find")
String getValue()
{
return applicationProperties.getName();
}
}
Run the spring boot application
Call localhost:XXXX/find from your browser
Output : xyz
Change the value in application.properties from xyz to abc
Using postman send a put /options request to localhost:XXXX/actuator/refresh
--Note this request should be either PUT/OPTIONS
Call localhost:XXXX/find from your browser
Output : abc
I think, in this case, it is advisable to keep it in the database so that, it can be changed & accessed seamlessly. We have a similar scenario where we keep the encrypted password for database in the properties file. While connecting to db, it needs to be decrypted. We do that by extending PropertyPlaceholderConfigurer as follows.
public class MyPropertyConfigurer extends PropertyPlaceholderConfigurer{
protected void convertProperties(Properties props){
Enumeration<?> propertyNames = props.propertyNames();
while (propertyNames.hasMoreElements()) {
String propertyName = (String) propertyNames.nextElement();
String propertyValue = props.getProperty(propertyName);
if(propertyName.indexOf("db.password") != -1){
decryptAndSetPropValue(props,propertyName,propertyValue);
}
}
}
}
But, this is done only once while loading the properties file.
Is there any way to load a class marked with #ConfigurationProperties without using a Spring Context directly? Basically I want to reuse all the smart logic that Spring does but for a bean I manually instantiate outside of the Spring lifecycle.
I have a bean that loads happily in Spring (Boot) and I can inject this into my other Service beans:
#ConfigurationProperties(prefix="my")
public class MySettings {
String property1;
File property2;
}
See the spring docco for more info http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#boot-features-external-config-command-line-args
But now I need to access this bean from a class that is created outside of Spring (by Hibernate). The class is created so early in the app startup process that Spring Boot has not yet made the application context available through the classic lookup helper methods or roll-my-own static references.
So I instead want to do something like:
MySettings mySettings = new MySettings();
SpringPropertyLoadingMagicClass loader = new SpringPropertyLoadingMagicClass();
loader.populatePropertyValues(mySettings);
And have MySettings end up with all its values loaded, from the command line, system properties, app.properties, etc. Is there some class in Spring that does something like this or is it all too interwoven with the application context?
Obviously I could just load the Properties file myself, but I really want to keep Spring Boot's logic around using command line variables (e.g. --my.property1=xxx), or system variables, or application.properties or even a yaml file, as well as its logic around relaxed binding and type conversion (e.g. property2 is a File) so that it all works exactly the same as when used in the Spring context.
Possible or pipe dream?
Thanks for your help!
I had the same "issue".
Here is how I solved it in SpringBoot version 1.3.xxx and 1.4.1.
Let's say we have the following yaml configuration file:
foo:
apis:
-
name: Happy Api
path: /happyApi.json?v=bar
-
name: Grumpy Api
path: /grumpyApi.json?v=grrr
and we have the following ConfigurationProperties:
#ConfigurationProperties(prefix = "foo")
public class ApisProperties {
private List<ApiPath> apis = Lists.newArrayList();
public ApisProperties() {
}
public List<ApiPath> getApis() {
return apis;
}
public static class ApiPath {
private String name;
private String path;
public String getName() {
return name;
}
public void setName(final String aName) {
name = aName;
}
public String getPath() {
return path;
}
public void setPath(final String aPath) {
path = aPath;
}
}
}
Then, to do the "magic" things of Spring Boot programmatically (e.g. loading some properties in a static method), you can do:
private static ApisProperties apiProperties() {
try {
ClassPathResource resource;
resource = new ClassPathResource("/config/application.yml");
YamlPropertiesFactoryBean factoryBean;
factoryBean = new YamlPropertiesFactoryBean();
factoryBean.setSingleton(true); // optional depends on your use-case
factoryBean.setResources(resource);
Properties properties;
properties = factoryBean.getObject();
MutablePropertySources propertySources;
propertySources = new MutablePropertySources();
propertySources.addLast(new PropertiesPropertySource("apis", properties));
ApisProperties apisProperties;
apisProperties = new ApisProperties();
PropertiesConfigurationFactory<ApisProperties> configurationFactory;
configurationFactory = new PropertiesConfigurationFactory<>(apisProperties);
configurationFactory.setPropertySources(propertySources);
configurationFactory.setTargetName("foo"); // it's the same prefix as the one defined in the #ConfigurationProperties
configurationFactory.bindPropertiesToTarget();
return apisProperties; // apiProperties are fed with the values defined in the application.yaml
} catch (BindException e) {
throw new IllegalArgumentException(e);
}
}
Here's an update to ctranxuan's answer for Spring Boot 2.x. In our situation, we avoid spinning up a Spring context for unit tests, but do like to test our configuration classes (which is called AppConfig in this example, and its settings are prefixed by app):
public class AppConfigTest {
private static AppConfig config;
#BeforeClass
public static void init() {
YamlPropertiesFactoryBean factoryBean = new YamlPropertiesFactoryBean();
factoryBean.setResources(new ClassPathResource("application.yaml"));
Properties properties = factoryBean.getObject();
ConfigurationPropertySource propertySource = new MapConfigurationPropertySource(properties);
Binder binder = new Binder(propertySource);
config = binder.bind("app", AppConfig.class).get(); // same prefix as #ConfigurationProperties
}
}
The "magic" class you are looking for is PropertiesConfigurationFactory. But I would question your need for it - if you only need to bind once, then Spring should be able to do it for you, and if you have lifecycle issues it would be better to address those (in case they break something else).
This post is going into similar direction but extends the last answer with also validation and property placeholder resolutions.
Spring Boot Binder API support for #Value Annotations
#Value annotations in ConfigurationPropertys don't seem to bind properly though (at least if the referenced values are not part of the ConfigurationProperty's prefix namespace).
import org.springframework.boot.context.properties.bind.Binder
val binder = Binder.get(environment)
binder.bind(prefix, MySettings.class).get
I would like to have a properties setup which can, on certain environments, override specific properties. For example, our default JDBC properties for dev are:
db.driverClassName=com.mysql.jdbc.Driver
db.url=jdbc:mysql://localhost:3306/ourdb
db.username=root
db.password=
The problem is that some of our devs would like to have a different username/password on the db, or possibly even a non locally hosted db. The same is true for our rabbitMQ configuration, which currently uses a similar localhost, guest/guest setup. Being able to override the properties of certain elements of this configuration on a per-developer basis would allow us to move much of the infrastructure/installation requirements for building the software off the local machine and onto dedicated servers.
I have set-up a simple project to wrap my head around the configuration required to achieve what I want, and this is my first foray into the world of spring property configuration, since up till now, property loading and management is done with some custom code. Here is my setup:
class Main_PropertyTest {
public static void main(String[] args) {
String environment = System.getenv("APPLICATION_ENVIRONMENT"); // Environment, for example: "dev"
String subEnvironment = System.getenv("APPLICATION_SUB_ENVIRONMENT"); // Developer name, for example: "joe.bloggs"
System.setProperty("spring.profiles.active", environment);
System.setProperty("spring.profiles.sub", subEnvironment);
try(AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(PropertyTestConfiguration.class)) {
Main_PropertyTest main = context.getBean(Main_PropertyTest.class);
main.printProperty();
}
}
private final String property;
public Main_PropertyTest(String property) {
this.property = property;
}
public void printProperty() {
System.out.println("And the property is: '" + property + "'.");
}
}
And my configuration:
#Configuration
public class PropertyTestConfiguration {
#Bean
public static PropertySourcesPlaceholderConfigurer primaryPlaceholderConfigurer() {
PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();
propertySourcesPlaceholderConfigurer.setLocation(new ClassPathResource(System.getProperty("spring.profiles.active") + ".main.properties"));
return propertySourcesPlaceholderConfigurer;
}
#Bean
public static PropertySourcesPlaceholderConfigurer secondaryPlaceholderConfigurer() {
PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();
propertySourcesPlaceholderConfigurer.setLocation(new ClassPathResource(System.getProperty("spring.profiles.sub") + ".main.properties"));
propertySourcesPlaceholderConfigurer.setIgnoreResourceNotFound(true);
propertySourcesPlaceholderConfigurer.setIgnoreResourceNotFound(true);
propertySourcesPlaceholderConfigurer.setOrder(-1);
return propertySourcesPlaceholderConfigurer;
}
#Bean
public Main_PropertyTest main_PropertyTest(#Value("${main.property}") String property) {
Main_PropertyTest main_PropertyTest = new Main_PropertyTest(property);
return main_PropertyTest;
}
}
And for completeness, my dev.main.properties and test.main.properties:
main.property=dev
main.property=test
The main problem is that I get an illegal argument exception. As far as I can tell, what I have written should be the javaconfig equivalent of this method: http://taidevcouk.wordpress.com/2013/07/04/overriding-a-packaged-spring-application-properties-file-via-an-external-file/
Unfortunately I get the following error: java.lang.IllegalArgumentException: Could not resolve placeholder 'main.property' in string value "${main.property}". Note that I also need to take care of the case where there is no sub-environment, and this is the case I have started with (although I get the same error even if both files exist). If I remove the bean which sets up the second propertysourcesplaceholderconfigurer, then it all works fine (by which I mean dev.main.properties is loaded and "And the property is: 'dev'." is printed out).
A secondary problem is that the code doesn't look great, and each layer of the system will need two PSPC's set-up so that they can access these properties. Furthermore, it requires a lot of manual calls to System.getProperty(), since I couldn't pass ${spring.profiles.active} to PSPC.setLocation();
Note: I have tried #PropertySources({primaryproperties, secondaryProperties}), but this fails because secondaryProperties does not exist. I have also tried #Autowired Environment environment; and getting the properties from that, but the secondary PSPC causes the environment to not be autowired...
So following this lengthy explanation, my questions are:
Is this the right way of solving this problem?
If so, what is wrong with my configuration?
How can I simplify the configuration (if at all)?
Is there an alternative mechanism available which would solve my problem?
Thank you for your time! :)
Your configuration is flawed when configuring BeanFactoryPostProcessor with java config the methods should be static. However it can be even easier, instead of registering your own PropertySourcesPlaceholderConfigurer utilize the default #PropertySource support.
Rewerite your jav config to the following
#Configuration
#PropertySource(name="main", value= "${spring.profiles.active}.main.properties")
public class PropertyTestConfiguration {
#Autowired
private Environment env;
#PostConstruct
public void initialize() {
String resource = env.getProperty("spring.profiles.sub") +".main.properties";
Resource props = new ClassPathResource(resource);
if (env instanceof ConfigurableEnvironment && props.exists()) {
MutablePropertySources sources = ((ConfigurableEnvironment) env).getPropertySources();
sources.addBefore("main", new ResourcePropertySource(props));
}
}
#Bean
public Main_PropertyTest main_PropertyTest(#Value("${main.property}") String property) {
Main_PropertyTest main_PropertyTest = new Main_PropertyTest(property);
return main_PropertyTest;
}
}
This should first load the dev.main.properties and additionally the test.main.properties which will override the earlier loaded properties (when filled ofcourse).
I had a similar issue with overwriting already existing properties in integration tests
I came up with this solution:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = {
SomeProdConfig.class,
MyWebTest.TestConfig.class
})
#WebIntegrationTest
public class MyWebTest {
#Configuration
public static class TestConfig {
#Inject
private Environment env;
#PostConstruct
public void overwriteProperties() throws Exception {
final Map<String,Object> systemProperties = ((ConfigurableEnvironment) env)
.getSystemProperties();
systemProperties.put("some.prop", "test.value");
}
}