I have spring-boot application. application.xml and some annotated methods.
In case string properties from application.xml is OK. For example:
#KafkaListener(id = "${app.kafka.group}", topics = {"${app.kafka.topic}"}, containerFactory = "singleFactoryCap", autoStartup = "true")
public void consume(Event event) throws ParseException { ...
But how to put INTEGER property from application.xml into an annotation parameter of method?
#Scheduled(initialDelay = ???"${app.config.initialDelay:5000}"???, fixedDelay = ???"${app.config.fixedDelay:5000}"???)
public void loadEvents() {
The #Scheduled annotation takes long properties initialDelay, fixedRate and fixedDelay if you want to hard-code the values but the annotation also provides the properties initialDelayString, fixedRateString and fixedDelayString that you can use as an alternative if you want to configure the behaviour using external configuration properties.
Related
I am trying to use the RepliyngKafkaTemplate like I managed to use the KafkaTemplate in a REST controller.
#RestController
public class TestController {
#Autowired
private ReplyingKafkaTemplate<Object, KafkaExampleRecord, KafkaExampleRecord> replyingTemplate;
#PostMapping("/test/request")
public void requestReply(#RequestBody KafkaExampleRecord record) throws ExecutionException, InterruptedException, TimeoutException {
ProducerRecord<Object, KafkaExampleRecord> producerRecord = new ProducerRecord<>("mytopic", record);
RequestReplyFuture<Object, KafkaExampleRecord, KafkaExampleRecord> replyFuture = replyingTemplate.sendAndReceive(producerRecord);
SendResult<Object, KafkaExampleRecord> sendResult = replyFuture.getSendFuture().get(10, TimeUnit.SECONDS);
ConsumerRecord<Object, KafkaExampleRecord> consumerRecord = replyFuture.get(10, TimeUnit.SECONDS);
}
}
However I am getting the following exception.
Field replyingTemplate in com.blah.KafkaController required a bean of type 'org.springframework.kafka.requestreply.ReplyingKafkaTemplate' that could not be found.
I enabled auto configuration like this.
#Configuration
#EnableKafka
public class KafkaConfig {
}
All Kafka settings are in my application.yml.
What else do I need? Do I really have to define beans? Seems unnecessary.
Do I really have to define beans? Seems unnecessary.
Yes, you have to declare a beans for the replying template (including the reply container); Spring Boot only auto configures a simple KafkaTemplate.
Can you check, whether you are scanning the basePackages correctly. Sometimes, you may end-up with this issue, if you not scanning the packages correctly, and I have experienced this many times in the Spring Boot application.
#ComponentScan(
basePackages = {
"x.x.x.x"
}
)
If I have some global config properties value that want to set on application start up, one of the ways to do is by setting it in application.properties and then using #Value to inject those values. However, if I want to set the values by making an API call to get those properties value on application start up and then set the values (but want to use similar way as #Value), rather than getting and setting it via properties files, how should it be achieved ?
#Configuration
public class config {
#Value("${properties1}")
private String properties1;
#Value("${properties2}")
private String properties2;
}
I have done some web search on custom property source (https://projects.spring.io/spring-cloud/spring-cloud.html#customizing-bootstrap-property-sources), and tried to follow the example, but encountered the error that the placeholder could not be resolved. How to get back the value ?
Could not resolve placeholder 'property.from.sample.custom.source' in value "${property.from.sample.custom.source}"
#Configuration
public class CustomPropertySourceLocator implements PropertySourceLocator {
#Override
public PropertySource<?> locate(Environment environment) {
return new MapPropertySource("customProperty",
Collections.<String, Object>singletonMap("property.from.sample.custom.source", "worked as intended"));
}
}
#Service
public class MainService {
#Value("${property.from.sample.custom.source}")
private String value;
public void printValue() {
System.out.println("value - " + value);
}
}
Assuming you use Spring Boot, you can run the Spring Boot application and pass the arguments using the following Maven command (depends on the Spring Boot version):
Spring Boot 1.x: using -Drun.arguments:
spring-boot:run -Drun.arguments=--properties1=One,--properties2=Two
Spring Boot 2.x: using -Dspring-boot.run.arguments:
spring-boot:run -Dspring-boot.run.arguments=--properties1=One,--properties2=Two
Now you can access the values using the #Value annotation:
#Value("${properties1}")
private String properties1;
#Value("${properties2}")
private String properties2;
Note: Once the properties are defined in the properties files (ex. application.properties and/or application-dev.yml etc..), defined in the command line like above, they can be accessed through the #Value annotation.
Baeldung's website offers a nice article: Command-Line Arguments in Spring Boot.
I am not sure if we can set values to properties file after application is up. Because properties file will be injected to #Bean when application is running. But we can hack this.
First, Create a file that contains all the configuration file which will be load from properties file. This file will be our template and initial / default values of the configuration.
Let say we have below configuration as application.yml
config:
name: Anna
age: 18
Then create configuration file
#ConfigurationProperties(prefix = "config")
public class ConfigProperties {
private String name;
private Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
Don't forget to modify our Application class
#SpringBootApplication
#EnableConfigurationProperties(ConfigProperties::class)
public class Application { ... }
Through here, we can just #Autowire the config class to get the value we want, rather than using a #Value.
Through here we already have the properties value to our config class. Then if you want to update the properties value (now already on config file) through an API, do a simple setName(...).
But, we have to be aware that this way is only work on a single run. When we restart the application, the value will use the default from properties file.
In spring boot application, I define some config properties in yaml file as below.
my.app.maxAttempts = 10
my.app.backOffDelay = 500L
And an example bean
#ConfigurationProperties(prefix = "my.app")
public class ConfigProperties {
private int maxAttempts;
private long backOffDelay;
public int getMaxAttempts() {
return maxAttempts;
}
public void setMaxAttempts(int maxAttempts) {
this.maxAttempts = maxAttempts;
}
public void setBackOffDelay(long backOffDelay) {
this.backOffDelay = backOffDelay;
}
public long getBackOffDelay() {
return backOffDelay;
}
How can I inject the values of my.app.maxAttempts and my.app.backOffdelay to Spring Retry annotation? In the example below, I want to replace the value 10 of maxAttempts and 500Lof backoff value with the corresponding references of config properties.
#Retryable(maxAttempts=10, include=TimeoutException.class, backoff=#Backoff(value = 500L))
Staring from spring-retry-1.2.0 we can use configurable properties in #Retryable annotation.
Use "maxAttemptsExpression", Refer the below code for usage,
#Retryable(maxAttemptsExpression = "#{${my.app.maxAttempts}}",
backoff = #Backoff(delayExpression = "#{${my.app. backOffDelay}}"))
It will not work if you use any version less than 1.2.0.Also you don't require any configurable property classes.
You can also use existing beans in expression attributes.
#Retryable(include = RuntimeException.class,
maxAttemptsExpression = "#{#retryProperties.getMaxAttempts()}",
backoff = #Backoff(delayExpression = "#{#retryProperties.getBackOffInitialInterval()}",
maxDelayExpression = "#{#retryProperties.getBackOffMaxInterval" + "()}",
multiplierExpression = "#{#retryProperties.getBackOffIntervalMultiplier()}"))
String perform();
#Recover
String recover(RuntimeException exception);
where
retryProperties
is your bean which holds retry related properties as in your case.
You can use Spring EL as shown below to load the properties:
#Retryable(maxAttempts="${my.app.maxAttempts}",
include=TimeoutException.class,
backoff=#Backoff(value ="${my.app.backOffDelay}"))
I have a few different #Configuration classes, each of which corresponds to a different Spring Batch job, i.e., one Job bean exists in each configuration and each Step, Tasklet, etc. required for a given job exists in the same configuration class as that job. Example:
#Configuration
public class DemoJobConfiguration(JobBuilderFactory jobBuilderFactory) {
#Bean
public Job demoJob() {
return jobBuilderFactory.get("demoJob").start(...).build();
}
}
#Configuration
public class TestJobConfiguration(JobBuilderFactory jobBuilderFactory) {
#Bean
public Job testJob() {
return jobBuilderFactory.get("testJob").start(...).build();
}
}
The application is a command-line application. The first argument is the name of the job to run. The associated Job bean is retrieved based on that argument and then is executed with a JobLauncher. Example:
#Override
public void run(String... args) throws Exception {
String jobName = args[0];
Job job = prepareJob(jobName); //gets the Job from the application context
JobParameters jobParameters = prepareJobParameters(args); //sets args[1], etc. to JobParameter objects
JobExecution result = jobLauncher.run(job, jobParameters);
}
What I'd like to know is if there's a way to use a #Conditional annotation (or something else) to only load a configuration class if args[0] is a certain value, e.g.,
#Configuration
#Conditional("\"testJob\".equals(args[0])")
public class TestJobConfiguration(JobBuilderFactory jobBuilderFactory) {
...
}
The advantage to this would be that only beans relevant to the job being run are ever loaded into memory and beans corresponding to other jobs are never loaded. This would be majorly helpful as more jobs get added to the project.
Is loading configurations based on command line arguments possible? Has it been done before? An hour of googling didn't turn up anything but I'm still hopeful that there's a way to accomplish this.
I figured out the answer to my own question.
Solution:
Include a command line argument in the form --jobName=testJob. Spring boot will automatically load that into the Environment (https://docs.spring.io/spring-boot/docs/1.0.1.RELEASE/reference/html/boot-features-external-config.html)
Use the #ConditonalOnProperty annotation like so:
#ConditionalOnProperty(value = "jobName", havingValue = "testJob")
public class TestJobConfiguration {
...
}
If you want more control over Conditional Configuration based on Command Line arguments you can create a custom Condition that can use the the ApplicationArguments class to fully parse the Options:
public class CustomCommandLineArgsCondition implements Condition {
#Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
ApplicationArguments args = context.getBeanFactory().getBean(ApplicationArguments.class);
//Do something with the command line arguments.
return true;
}
}
You then annotate your configuration class with #Conditional(CustomCommandLineArgsCondition.class)
In spring boot application, I define some config properties in yaml file as below.
my.app.maxAttempts = 10
my.app.backOffDelay = 500L
And an example bean
#ConfigurationProperties(prefix = "my.app")
public class ConfigProperties {
private int maxAttempts;
private long backOffDelay;
public int getMaxAttempts() {
return maxAttempts;
}
public void setMaxAttempts(int maxAttempts) {
this.maxAttempts = maxAttempts;
}
public void setBackOffDelay(long backOffDelay) {
this.backOffDelay = backOffDelay;
}
public long getBackOffDelay() {
return backOffDelay;
}
How can I inject the values of my.app.maxAttempts and my.app.backOffdelay to Spring Retry annotation? In the example below, I want to replace the value 10 of maxAttempts and 500Lof backoff value with the corresponding references of config properties.
#Retryable(maxAttempts=10, include=TimeoutException.class, backoff=#Backoff(value = 500L))
Staring from spring-retry-1.2.0 we can use configurable properties in #Retryable annotation.
Use "maxAttemptsExpression", Refer the below code for usage,
#Retryable(maxAttemptsExpression = "#{${my.app.maxAttempts}}",
backoff = #Backoff(delayExpression = "#{${my.app. backOffDelay}}"))
It will not work if you use any version less than 1.2.0.Also you don't require any configurable property classes.
You can also use existing beans in expression attributes.
#Retryable(include = RuntimeException.class,
maxAttemptsExpression = "#{#retryProperties.getMaxAttempts()}",
backoff = #Backoff(delayExpression = "#{#retryProperties.getBackOffInitialInterval()}",
maxDelayExpression = "#{#retryProperties.getBackOffMaxInterval" + "()}",
multiplierExpression = "#{#retryProperties.getBackOffIntervalMultiplier()}"))
String perform();
#Recover
String recover(RuntimeException exception);
where
retryProperties
is your bean which holds retry related properties as in your case.
You can use Spring EL as shown below to load the properties:
#Retryable(maxAttempts="${my.app.maxAttempts}",
include=TimeoutException.class,
backoff=#Backoff(value ="${my.app.backOffDelay}"))