use of #Value Spring annotation during Controller initialization issue - spring-boot

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) {
...

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;

How to make Set using spring-data-aerospike

Environment:
spring-boot v2.0.4 RELEASE
spring-data-aerospike v2.0.1.RELEASE
java - 8
Here are my application code and properties.
// application.properties
aerospike.hosts=xxx:3000
aerospike.namespace=test
// aerospike configuration class
#Configuration
#RequiredArgsConstructor
#EnableConfigurationProperties(AerospikeConfiguration.AerospikeConfigurationProperties.class)
#EnableAerospikeRepositories(basePackageClassses = TestAeroRepository.class)
public class AerospikeConfiguration extends AbstractAerospikeDataConfiguration {
private final AerospikeConfigurationProperties aerospikeConfigurationProperties;
#Override
protected Collection<Host> getHosts() {
return Host.parseServiceHosts(aerospikeConfigurationProperties.getHosts());
}
#Override
protected String nameSpace() {
return aerospikeConfigurationProperties.getNamespace();
}
#Data
#Validate
#ConfigurationProperties("aerospike")
public static class AerospikeConfigurationProperties {
#NotEmpty
String hsots;
#NotEmpty
String namespace;
}
}
# Entity class
#Value
#Document
#Builder(toBuilder = true)
#AllArgsConstructor
public class testEntity() {
#Id
int id;
#Field
String name;
#Field
String timestamp;
}
#Repository
public interface TestAeroRepository extends AerospikeRepository<TestEntity, Integer> {
}
public interface TestAeroService {
void save();
}
#Service
#RequiredArgsConstructor
public class TestAeroServiceImpl implements TestAeroService {
private final TestAeroRepository testAeroRepository;
#Override
public void save(TestEntity entity) {
testAeroRepository.save(entity);
}
}
I checked Aerospike client connection has no problem.
But error is occurred when save() method is executed.
org.springframework.cglib.core.ReflectUtils.defineClass(Ljava/lang/String;[BLjava/lang/ClassLoader;Ljava/security/ProtectionDomain;Ljava/lang/Class;)Ljava/lang/Class;
Have to make sets before execute the application? I didn't make sets.
Any problem with my code?
You’re using an old version of spring-data-aerospike (2.0.1.RELEASE was released on April 2019) is there any chance you can upgrade to the latest version? 2.4.2.RELEASE
You can see how to setup a simple spring data aerospike application here: https://medium.com/aerospike-developer-blog/simple-web-application-using-java-spring-boot-aerospike-database-and-docker-ad13795e0089
Please share the entire project’s code and the entire exception.
I would look into:
The configuration class (The Aerospike Beans creation).
The content of the testEntity class - are you using #Id annotation on the primary key field?
Extending the repository class with specifying the testEntity object (… extends AerospikeRepository<testEntity, Object> {) you can see an example in the link I added.
The set is automatically created and takes the name of your object class, which is testEntity in your case. For example, based on your code, if you do not specify a collection in the #Document annotation a set named "testEntity" will automatically be created. I added the #Document(collection = "testEntitys") annotation and all I did was create two set. Once you insert your first record, run the "SHOW SETS" aql command and it will be there. So that's one way to do it.

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

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.

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