Spring annotation value from configuration - spring

In my Spring Boot application I have configured following JMS Listener:
#Component
public class Consumer {
#JmsListener(destination = "image.index.queue")
public void receiveQueue(IndexRequest indexRequest) {
...
}
}
How to supply destination name image.index.queue from configuration (e.g. application.properties) instead of a hardcoded value?

import org.springframework.beans.factory.annotation.Value;
#JmsListener(destination = #Value("${jmx.image.index.queue}")
public void receiveQueue(IndexRequest indexRequest) {
...
}
And in your properties file
jmx.image.index.queue=image.index.queue

You need to indicate to Spring that it's a placeholder value, so instead of:
#JmsListener(destination = "image.index.queue")
Use:
#JmsListener(destination = "${image.index.queue}")
If needed, you can also use SpEL but this shouldn't be necessary if using application.properties. E.g.:
#JmsListener(destination = "#{systemProperties['image.index.queue']}")
Verified as of Spring Boot 2.0.0.RELEASE

Related

Spring boot: how to read specific property from application.yml and ignore others

My goal is to read some property from application.yml file and ignore other properties. This is need for testing purposes - I want to read part of config and validate it. I want to ignore other properties because Spring Boot tring to handle it but this is no need for my test.
Example.
Config:
first:
property:
{cipher}value #for example Spring trying to handle cipher and throws exception
second:
property:
value
Class (using String for simplification):
#Configuration
public class Configuration {
#Bean
#ConfigurationProperties(prefix = "second.property")
public String secondProperty() {
return new String();
}
}
Test class:
#SpringBootTest
public class ConfigPropertiesCheckerIntegrationTest {
#Autowired
String secondProperty;
#Test
void checkAutoConfigEndpointProperties() {
assertThat(secondProperty, is(notNullValue()));
}
}
So question: how to ignore first.property and say Spring just skip it?
You can disable {cipher}ed properties decryption with spring.cloud.decrypt-environment-post-processor.enabled=false. E.g:
#SpringBootTest(properties = "spring.cloud.decrypt-environment-post-processor.enabled=false")

How to validate Spring Boot's External Configuration used in Placeholders

I am aware of possibility to validate the external configuration of a Spring Boot configuration done by the help of configuration properties (#ConfigurationProperties) and bean validation.
But how can I validate properties used in placeholders like in this code snippet:
#Component
public class Receiver {
#JmsListener(destination = "${jms.queuename}", containerFactory = "myFactory")
public void receiveMessage(Email email) {
System.out.println("Received <" + email + ">");
}
}
The only workaround I can imagine is to use the same property also in a configuration properties class like this.
#ConfigurationProperties(prefix = "jms")
#Validated
public class JMSProperties {
#NotNull
String queuename;
}
Any hint?

How to config max-delivery-attempts for Spring Boot with Embedded ActiveMQ Artemis?

I would like to config the max-delivery-attempts of dead letter as described in the manual.
I tried Spring Boot with embedded ActiveMQ Artemis JMS server, but cannot figure out how to set max-delivery-attempts value.
After debugging the Configuration instance I finally find the method. Here's the code:
#Configuration
public class RedeliveryConfiguration implements ArtemisConfigurationCustomizer {
#Override
public void customize(org.apache.activemq.artemis.core.config.Configuration configuration) {
Map<String, AddressSettings> addressesSettings = configuration.getAddressesSettings();
// # is the catch all address or default address
addressesSettings.get("#").setMaxDeliveryAttempts(2);
}
}

Do not want to load application.yml when testing with Spring Boot

How can I tell Spring to load only application-test.yml and not application.yml file ?
I have a config class:
#ActiveProfiles("test")
#SpringBootConfiguration
public class MongoTestConfig {
#Autowired
MongoOperations operations;
...
}
And a test class:
#RunWith(SpringRunner.class)
#DataMongoTest
#SpringBootTest(classes = MongoTestConfig.class)
public class TagDefinitionRepositoryTest {
...
#Test
....
}
I've tried to add :
#TestPropertySource(locations = {"classpath:application-test.yml"})
#ContextConfiguration(initializers = ConfigFileApplicationContextInitializer.class)
To my config class but it doesn't work: Spring still load application.yml
I don't think you can tell Spring Boot to ignore application.yml completely. What you can do though is to override all the non desired properties using test specific property files.
Based on the code snippet you posted, any property in application-test.yml will override the equivalent property in application.yml.
Spring Boot considers application-test.yml specific to the profile "test" (which has a higher priority over the default application.yml). No annotation #TestPropertySource is needed.
But if you want to choose another name for your properties file, then you can use #TestProertySource, since files indicated in #TestProperySource parameters have higher priority over the others.
You might want to have a look at Spring Boot external configuration rules for resolving properties
I've end up using #SpringBootTest instead of #DataMongoTest
#SpringBootConfiguration
#ComponentScan(basePackages = {"com.package.services"})
#EnableMongoRepositories(basePackages = {"com.package.repositories"})
public class MongoTestConfig {
private static final MongodStarter starter = MongodStarter.getDefaultInstance();
#Bean
public MongoClient mongoClient() throws IOException {
MongodExecutable _mongodExe;
MongodProcess _mongod;
_mongodExe = starter.prepare(new MongodConfigBuilder()
.version(Version.Main.V3_2)
.net(new Net("localhost", 12345, Network.localhostIsIPv6()))
.build());
_mongod = _mongodExe.start();
MongoClient _mongo = new MongoClient("localhost", 12345);
return _mongo;
}
#Bean
public MongoDbFactory mongoDbFactory() throws IOException{
return new SimpleMongoDbFactory(mongoClient() , "test");
}
#Bean
public MongoTemplate mongoTemplate() throws IOException {
return new MongoTemplate(mongoDbFactory());
}
And my test class is:
#RunWith(SpringRunner.class)
#SpringBootTest
public class MyRepositoryTest {
...

#Value not resolved when using #PropertySource annotation. How to configure PropertySourcesPlaceholderConfigurer?

I have following configuration class:
#Configuration
#PropertySource(name = "props", value = "classpath:/app-config.properties")
#ComponentScan("service")
public class AppConfig {
and I have service with property:
#Component
public class SomeService {
#Value("#{props['some.property']}") private String someProperty;
I receive error when I want to test the AppConfig configuration class with
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'someService': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private java.lang.String service.SomeService.someProperty; nested exception is org.springframework.beans.factory.BeanExpressionException: Expression parsing failed; nested exception is org.springframework.expression.spel.SpelEvaluationException: EL1008E:(pos 0): Field or property 'props' cannot be found on object of type 'org.springframework.beans.factory.config.BeanExpressionContext'
The issue is documented in SPR-8539
but anyway I cannot figure out how to configure PropertySourcesPlaceholderConfigurer to get it work.
Edit 1
This approach works well with xml configuration
<util:properties id="props" location="classpath:/app-config.properties" />
but I want to use java for configuration.
as #cwash said;
#Configuration
#PropertySource("classpath:/test-config.properties")
public class TestConfig {
#Value("${name}")
public String name;
//You need this
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
If you use #PropertySource, properties have to be retrieved with:
#Autowired
Environment env;
// ...
String subject = env.getProperty("mail.subject");
If you want to retrieve with #Value("${mail.subject}"), you have to register the prop placeholder by xml.
Reason:
https://jira.springsource.org/browse/SPR-8539
I found the reason #value was not working for me is, #value requires PropertySourcesPlaceholderConfigurer instead of a PropertyPlaceholderConfigurer. I did the same changes and it worked for me, I am using spring 4.0.3 release. I configured this using below code in my configuration file.
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
Don't you need a method on your #Configuration class that returns PropertySourcesPlaceholderConfigurer, annotated #Bean and is static, to register any #PropertySource with Spring?
http://www.baeldung.com/2012/02/06/properties-with-spring/#java
https://jira.springsource.org/browse/SPR-8539
I had the very same problem. #PropertySource is not playing well with #Value. A quick workaround is to have an XML configuration which you'll refer to it from your Spring Java Configuration using #ImportResource as usual and that XML configuration file will include a single entry: <context:property-placeholder /> (of course with the needed namespace ceremony). Without any other change #Value will inject properties in your #Configuration pojo.
This can also be configured in java this way
#Bean
public static PropertySourcesPlaceholderConfigurer properties() {
PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer();
configurer.setIgnoreUnresolvablePlaceholders(true);
configurer.setIgnoreResourceNotFound(true);
return configurer;
}
That looks mighty complicated, can't you just do
<context:property-placeholder location="classpath:some.properties" ignore-unresolvable="true"/>
then in code reference:
#Value("${myProperty}")
private String myString;
#Value("${myProperty.two}")
private String myStringTwo;
where some.properties looks something like this
myProperty = whatever
myProperty.two = something else\
that consists of multiline string
For java based config you can do this
#Configuration
#PropertySource(value="classpath:some.properties")
public class SomeService {
And then just inject using #value as before
Since Spring 4.3 RC2 using PropertySourcesPlaceholderConfigurer or <context:property-placeholder> is not needed anymore. We can use directly #PropertySource with #Value. See this Spring framework ticket
I have created a test application with Spring 5.1.3.RELEASE.
The application.properties contains two pairs:
app.name=My application
app.version=1.1
The AppConfig loads the properties via #PropertySource.
package com.zetcode.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
#Configuration
#PropertySource(value = "application.properties", ignoreResourceNotFound = true)
public class AppConfig {
}
The Application injects the properties via #Value and uses them.
package com.zetcode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
#ComponentScan(basePackages = "com.zetcode")
public class Application {
private static final Logger logger = LoggerFactory.getLogger(Application.class);
#Value("${app.name}")
private String appName;
#Value("${app.version}")
private String appVersion;
public static void main(String[] args) {
var ctx = new AnnotationConfigApplicationContext(Application.class);
var app = ctx.getBean(Application.class);
app.run();
ctx.close();
}
public void run() {
logger.info("Application name: {}", appName);
logger.info("Application version: {}", appVersion);
}
}
The output is:
$ mvn -q exec:java
22:20:10.894 [com.zetcode.Application.main()] INFO com.zetcode.Application - Application name: My application
22:20:10.894 [com.zetcode.Application.main()] INFO com.zetcode.Application - Application version: 1.1
The thing is: as far as I get it, <util:propertes id="id" location="loc"/>, is just a shorthand for
<bean id="id" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="location" value="loc"/>
</bean>
(see documentation of util:properties). Thus, when you use util:properties, a standalone bean is created.
#PropertySource, on the other hand, as documentation says is an
annotation providing a convenient and declarative mechanism for
adding a PropertySource to Spring's Environment'.
(see #PropertySource doc). So it doesn't create any bean.
Then "#{a['something']}" is a SpEL expression (see SpEL), that means "get something from bean 'a'". When util:properties is used, the bean exists and the expression is meaningful, but when #PropertySource is used, there is no actual bean and the expression is meaningless.
You can workaround this either by using XML (which is the best way, I think) or by issuing a PropertiesFactoryBean by yourself, declaring it as a normal #Bean.
Another thing that may be happening: ensure your #Value annotated values are not static.
In my case, depends-on="bean1" was within property-placeholder was causing the issue. I removed that dependency and used #PostConstruct to achieve the same original functionality and was able to read the new values too.
If you are configuring with xml, after adding
<context:property-placeholder location="..."/>
Make sure your annotations are activated. In my case properties were not fetched for this reason:
<context:annotation-config/>
for me it worked
spring.cloud.config.server.git.default-label=main
spring.cloud.config.server.native.searchLocations=file:///Users/${your username}/Projects/git-local-repositories/
spring.profiles.active=native

Resources