Why Spring KafkaTemplate doesn't set its instance variable "messageConverter" to volatile? - spring

I was reading source code of Spring KafkaTemplate(org.springframework.kafka.core) and came across this piece of code:
protected final Log logger = LogFactory.getLog(this.getClass()); //NOSONAR
private final ProducerFactory<K, V> producerFactory;
private final boolean autoFlush;
private final boolean transactional;
private final ThreadLocal<Producer<K, V>> producers = new ThreadLocal<>();
private RecordMessageConverter messageConverter = new MessagingMessageConverter();
private volatile String defaultTopic;
private volatile ProducerListener<K, V> producerListener = new LoggingProducerListener<K, V>();
As you see, variables like defaultTopic and producerListener are set to volatile which I presume to make them memory visible once being changed.
So I am confused why meesageConverter was not set to the same.

We generally don't expect configuration properties, such as the message converter to be changed at runtime so there is no need to make it volatile. If you have such a requirement, subclass the template and override the setter with a synchronized method (calling super.set...()).
You are looking at an older version of the code; those variables are no longer volatile.

Related

Spring RefreshScope with SpEL deferred Evaluation

I'm having trouble knowing how this is possible or a potential work around. I am looking at configuring variables that are in the form of "foo,bar,baz" into a List as separate elements. Currently the code looks like
#RefreshScope
#Configuration
#Getter
public class fakeConfiguration {
#Value("#{PropertySplitter.toSet('${properties:}')}")
private final Set<String> field = new HashSet<>();
}
#Component("PropertySplitter")
#SuppressWarnings("unused")
public class PropertySplitter {
public Set<String> toSet(String property) {
Set<String> set = new HashSet<>();
if(!property.trim().isEmpty()){
Collections.addAll(set, property.split("\\s*,\\s*"));
}
return set;
}
}
This properly evaluates the String list into a Set however the refreshScope never gets triggered properly. If I use
#Value("${properties:}")
private final String fieldAsString;
I can see the field properly refresh, but I'd like to actively convert the value to a list as it is changed. Is this possible?
In newer version of spring-boot below works for application.properties and Application.yml
#Value("#{${props.list}}")
private List<String> fieldList;
If you use Application.yml you can arrange the objects
props:
list:
- val1
- val2
and then use in code
#Value("${props.list}")
private List<String> ymlList
Last, You can try the below as well
#Value("#{'${props.list}'.split(',')}")
private List<String> myList;

use of #Value Spring annotation during Controller initialization issue

Probably someone else might have asked something similar as well, but I couldn't find an answer that provides a solution that works...
I'm in the process of learning spring boot and while I was playing with guava RateLimiter during my experiments I hit the following problem:
RateLimiter needs to be created during the Controller initialization, but if I want to load the rate I have to hardcode it, since if I try to load it from props using attributes with #Value Spring annotations it doesn't work.
Is there any "trick" around this "limitation"?
see code below:
#RestController
public class LoggingController {
private Logger logger = LoggerFactory.getLogger(LoggingController.class);
#Value("${count}")
private Double PERMITS_COUNT;
#Value("${seconds}")
private Double PERMITS_PER_SECONDS;
#Value("${consumed}")
private int PERMITS_CONSUMED;
//# Value fails here with NullPointerException
private RateLimiter rateLimiter = RateLimiter.create(PERMITS_COUNT / PERMITS_PER_SECONDS);
// This works file
private RateLimiter rateLimiter = RateLimiter.create(10d / 60d);
private AtomicInteger index = new AtomicInteger(0);
#GetMapping("/logging")
#ResponseBody
public String logging (#RequestParam(name="name", required=false, defaultValue="JavaBoss") String name) {
//#Value works fine if used here
rateLimiter.setRate(PERMITS_COUNT / PERMITS_PER_SECONDS);
rateLimiter.acquire(PERMITS_CONSUMED);
...
Many thanks in advance...
Use PostConstruct and you should be fine
#RestController
public class LoggingController {
private Logger logger = LoggerFactory.getLogger(LoggingController.class);
#Value("${count}")
private Double PERMITS_COUNT;
#Value("${seconds}")
private Double PERMITS_PER_SECONDS;
#Value("${consumed}")
private int PERMITS_CONSUMED;
private RateLimiter rateLimiter;
#PostConstruct
private void createRateLimiter() {
rateLimiter = RateLimiter.create(PERMITS_COUNT / PERMITS_PER_SECONDS);
}
private AtomicInteger index = new AtomicInteger(0);
#GetMapping("/logging")
#ResponseBody
public String logging (#RequestParam(name="name", required=false, defaultValue="JavaBoss") String name) {
...

Spring Data MongoDB - how to set maxTimeMS for Aggregation pipeline query

I've noticed that org.springframework.data.mongodb.core.aggregation.AggregationOption class covers just a small subset of these options described within MongoDB Aggregation pipeline documentation: https://docs.mongodb.com/manual/reference/command/aggregate/#syntax
I need to set maxTimeMS option, but it is not available within org.springframework.data.mongodb.core.aggregation.AggregationOption:
public class AggregationOptions {
private static final String BATCH_SIZE = "batchSize";
private static final String CURSOR = "cursor";
private static final String EXPLAIN = "explain";
private static final String ALLOW_DISK_USE = "allowDiskUse";
private static final String COLLATION = "collation";
private static final String COMMENT = "comment";
...
However, the another class (of mongodb-driver) actually has such field maxTimeMS,
com.mongodb.AggregationOptions:
public class AggregationOptions {
private final Integer batchSize;
private final Boolean allowDiskUse;
private final OutputMode outputMode;
private final long maxTimeMS;
private final Boolean bypassDocumentValidation;
private final Collation collation;
...
Any idea/tricks how to set this maxTimeMS for Aggregation query using Spring Data MongoDB API? Or maybe do I need to build/write such aggregation by using native query?
Btw. I know that Spring Data MongoDB supports maxTimeMS for find operations, for example:
Query.query(mongoCriteria).with(pageable).maxTime(Duration.ofMinutes(4))
However I need to set aggregation query processing timeout on server side in order to prevent "never ending" queries that kill performance.
spring-data-mongodb:2.2.0.RELEASE
I was trying to set a time limit to my query and it took me some time to find the answer; so, I want to share my method with anyone who might be interested:
AggregationOptions aggregationOptions = new AggregationOptions.Builder().maxTime(Duration.ofSeconds(3)).build();
Aggregation aggregate = Aggregation.newAggregation(match).withOptions(aggregationOptions);

How can I parse.Float an application properties?

My current code when I use inputstream. prop = new properties;
Application = prop.getProperty("Application");
servers = prop.getProperty("SERVERS");
username = prop.getProperty("USER_NAME");
password = prop.getProperty("PASSWORD");
Float criticalThreshold = Float.parseFloat(prop.getProperty("THRESHOLD_CRITICAL"));
Float badThreshold = Float.parseFloat(prop.getProperty("THRESHOLD_BAD"));
I recently implemented my application properties into my java class using spring boots way.
#Value("${Application}")
private String Application;
#Value("${SERVERS}")
private String servers;
#Value("${USER_NAME}")
private String username;
#Value("${PASSWORD}")
private String password;
But I do not know how to rewrite the Float.parseFloat
Float criticalThreshold = Float.parseFloat(prop.getProperty("THRESHOLD_CRITICAL"));
Float badThreshold = Float.parseFloat(prop.getProperty("THRESHOLD_BAD"));
I tried but it automatically gives me an compiler error.
#Value("${THRESHOLD_CRITICAL}")
private Float criticalThreshold;
#Value("${THRESHOLD_BAD}")
private Float badThreshold;
#Value lets you specify a method to call to alter the injected property:
#Value("#{T(java.lang.Float).parseFloat('${THRESHOLD_CRITICAL}')}")
private float criticalThreshold;
I tested it and it also works without the full package name:
#Value("#{T(Float).parseFloat('${THRESHOLD_CRITICAL}')}")
private float criticalThreshold;
You can refer the solution suggested by Alex as it does not require the additional variable like in the below approach.
You can't directly do that, but you can achieve that #PostConstruct and declaring one more variable criticalThresholdFloatValue like below:
#Value("${THRESHOLD_CRITICAL}")
private String criticalThreshold;
private float criticalThresholdFloatValue;
#PostConstruct
public void init() {
criticalThresholdFloatValue = Float.parseFloat(criticalThreshold);
}
Now, you can start using criticalThresholdFloatValue where ever you are using in the bean methods.

preprocess configuration values in Spring

In a Spring bean, I need to process a configuration property before using is, e.g.:
#Component
class UsersController {
#Value("${roles}")
private String rolesAsString;
private List<String> roles;
#PostConstruct
public void initRoles() {
// just an example, not necessarily string splitting
roles = rolesAsString.split(",");
}
This works, but I am left with an unneeded member variable 'rolesString'. What would be a clean concise way to only keep the processed value?
Properties is
roles=role1,role2,role3
Code is :
#Value("#{'${roles}'.split(',')}")
private List<String> roles;

Resources