#ConditionalOnExpression not working but #Value working - java

I have the need of enable/disable a #Aspect on a class in a Spring (non boot) application.
Spring version is: 4.1.6.RELEASE
I have a .properties file (that we already use successfully in other point of the application, for example to select the log4j2 configuration file) with the property aspect.enabled=true|false and I tried using the #ConditionalOnExpression annotation to enable|disable it.
This is my try:
application.properties file:
aspect.enabled=true
Class code:
#Configuration
#PropertySource(ignoreResourceNotFound = true, value = { "file:${catalina.home}/conf/application.properties" })
#ConditionalOnExpression("'${aspect.enabled}'=='true'")
#Aspect
#Component
public class TimingProfilerProduction {
#Value("${aspect.enabled}")
public String aspect;
With this configuration the expression is always evaluated to false.
I tried putting a single "true" to see if it works in this simple way:
#Configuration
#PropertySource(ignoreResourceNotFound = true, value = { "file:${catalina.home}/conf/application.properties" })
#ConditionalOnExpression("true")
#Aspect
#Component
public class TimingProfilerProduction {
#Value("${aspect.enabled}")
public String aspect;
Of course in this way the #ConditionalOnExpression gets evaluated to true and I can also prove that the aspect class property correctly reads the aspect.enabled property.
Try #3
I tried with a #ConditionalOnProperty:
#Configuration
#PropertySource(ignoreResourceNotFound = true, value = { "file:${catalina.home}/conf/application.properties" })
#ConditionalOnProperty(prefix="aspect", name="enabled", havingValue = "true")
but nothing, always false.
Try #4:
#ConditionalOnExpression("${aspect.enabled}")
or
#ConditionalOnExpression("!${aspect.enabled}")
Gives an error:
Caused by: org.springframework.expression.spel.SpelParseException: EL1041E:(pos 1): After parsing a valid expression, there is still more data in the expression: 'lcurly({)'
Try #5 (with default values):
#ConditionalOnExpression("${aspect.enabled:true}")
always gives true (even with aspect.enabled=false), and accordingly
#ConditionalOnExpression("${aspect.enabled:false}")
always gives false

Instead of:
#ConditionalOnExpression("'${aspect.enabled}' == 'true'")
Try:
#ConditionalOnExpression("${aspect.enabled} == true")

Related

Some condition from applecation.yaml in #EventListener

I would like the execution of the event handler to depend on whether the property is set to true or false in applecation.yaml file. I have three yaml files (test, dev, prod) and I have set the settings in them:
for application-dev.yml
page-cache:
starting: false
for application-test.yml
page-cache:
starting: true
for application-prod.yml
page-cache:
starting: true
And I need not to write 'dev' or 'test' in condition myself, but to read true or false from yaml files.
For example: condition = "#serviceEnabled == true" , does not work.
#Service
public class ServiceImpl{
#Value("${page-cache.starting}")
private Boolean serviceEnabled;
/means that when running dev will be false and the method will not run and this code is working
#EventListener(
value = ApplicationReadyEvent.class,
condition = "#environment.getActiveProfiles()[0] != 'dev'")
public void updateCacheAfterStartup() {
log.info("Info add starting...");
someService.getInfo();
}
I tried to do as in this article, but it doesn't work for me .
Evaluate property from properties file in Spring's #EventListener(condition = "...")
I also tried the same option
#Service
public class ServiceImpl{
// #Lazy private final ServiceImpl serviceImpl
#Value("${page-cache.starting}")
private Boolean serviceEnabled;
public Boolean isServiceEnabled() {
return this.serviceEnabled;
}
public Boolean getServiceEnabled() {
return serviceEnabled;
}
#EventListener(
value = ApplicationReadyEvent.class,
condition = "#ServiceImpl.serviceEnabled")
public void updateCacheAfterStartup() {
log.info("Info add starting...");
someService.getInfo();
}
Make sure the YAML format is correct in application-test.yml, application-prd.yml,...
example:
application-dev.yml
page-cache:
starting: false
ServiceImpl must be a component/bean, so annotate your class with #Service, #Component or use #Bean if the instance is created in a #Configuration class.
Extra tip:
You could use condition = "! #environment.acceptsProfiles('dev')"
instead of condition = "#environment.getActiveProfiles()[0] != 'dev'"
that way the order of active profiles does not matter
or define when the condition is valid: condition = "#environment.acceptsProfiles('test', 'prod')"
UPDATE:
You could also directly use page-cache.starting in the condition.
#EventListener(
value = ApplicationReadyEvent.class,
condition = "#environment.getProperty('page-cache.starting')")
public void updateCacheAfterStartup() {
// update the cache
}
Here updateCacheAfterStartup() will only be triggered at startup when page-cache.starting is true. If set to false or when not present the method will not be called.
If you want to force that page-cache.starting is provided (in all profiles) you should use: condition="#environment.getRequiredProperty('page-cache.starting')"
you can access the property using #Value in some class whose bean is created (either by annotations Component, Service, Configuration .. etc.). Use #EventListener(condition = "#beanName.youProperty"), the event is handled when the value of yourProperty is true or string having "true", "on", "yes", or "1" values.
if yourProperty is private and doesn't have getter also, then above will fail. yourProperty on the bean should be either public or should have getter if private.
in your case:
public class ServiceImpl{
#Value("${page-cache.starting}")
private Boolean serviceEnabled;
// can be getServiceEnabled
public Boolean isServiceEnabled() {
return this.serviceEnabled;
}
#EventListener(
value = ApplicationReadyEvent.class,
condition = "#serviceImpl.serviceEnabled")
public void updateCacheAfterStartup() {
log.info("Info add starting...");
someService.getInfo();
}

Unable to resolve variable from properties file when tried to access as function parameter using #Value annotation

This may be silly question to ask but i'm unable to find any satisfactory solution to my problem. In java we don't have the concept of default variables so i am trying to give default value from properties file to my function parameters/arguments using #Value annotation, but i'm always getting null and i'm unable to figure why is this happening. Please help me to solve the issue or provide me some appropriate link/reference which may solve my issue.
MainApplication.java
#SpringBootApplication
public class Application
{
public static void main(String[] args)
{
ApplicationContext context = SpringApplication.run(NetappApplication.class, args);
Sample sample = context.getBean(Sample.class);
System.out.println(sample.check(null));
}
}
Sample.java
public interface Sample
{
public String check(String message);
}
SampleImpl.java
#Service
#PropertySource("classpath:app.properties")
public class SampleImpl implements Sample
{
#Value("${test}")
String message1;
#Override
public String check(#Value("${test}") String message)
{
return message;
}
}
app.properties
test=anand
But you are passing null to your method...
Perhaps what you want to do is to assign default value to test in case it's not defined in property file:
#Value("${test:default}");
Then, when properties are autowired by Spring if placeholder resolver doesn't get the value from props file, it will use what is after :.
The best use case for this (that I can think of) is when you create Spring configuration.
Let's say you have a configuration class: for DB access. Simply put:
#Configuration
public class DbConfig {
#Value("${url:localhost}")
String dbUrl;
// rest for driver, user, pass etc
public DataSource createDatasource() {
// here you use some DataSourceBuilder to configure connection
}
}
Now, when Spring application starts up, properties' values are resolved, and as I wrote above you can switch between value from property and a default value. But it is done once, when app starts and Spring creates your beans.
If you want to check incoming argument on runtime, simple null check will be enough.
#Value("${test}")
String message1;
#Override
public String check(String message) {
if (message == null) {
return message1;
}
}

Spring #ConditionalOnProperty havingValue = "value1" or "value2"

I am looking for configurationOnProperty usage where I can specify to consider more than one value as shown below
Eg: #ConditionalOnProperty(value = "test.configname", havingValue = "value1" or "value2")
OR
I would like to know if it is possible to specify confiugrationOnProperty with condition of havingValue != "value3"
Eg: #ConditionalOnProperty(value = "test.configname", havingValue != "value3")
Please let me know if there is a way to achieve any one of the above in spring boot configuration.
Spring Boot provides AnyNestedCondition for created a condition that will match when any nested condition matches. It also provides AllNestedConditions and NoneNestedConditions for matching when all nested conditions or no nested conditions match respectively.
For your specific case where you want to match a value of value1 or value2 you would create an AnyNestedCondition like this:
class ConfigNameCondition extends AnyNestedCondition {
public ConfigNameCondition() {
super(ConfigurationPhase.PARSE_CONFIGURATION);
}
#ConditionalOnProperty(name = "test.configname", havingValue = "value1")
static class Value1Condition {
}
#ConditionalOnProperty(name = "test.configname", havingValue = "value2")
static class Value2Condition {
}
}
And then use it with #Conditional, like this for example:
#Bean
#Conditional(ConfigNameCondition.class)
public SomeBean someBean() {
return new SomeBean();
}
As shown in the javadoc for the nested condition annotations (linked to above) the nested conditions can be of any type. There's no need for them to all be of the same type as they are in this particular case.
The annotations #ConditionalOnProperty and #ConditionalOnExpression both do NOT have the java.lang.annotation.Repeatable annotation so you would not be able to just add multiple annotations for checking multiple properties.
The following syntax has been tested and works:
Solution for Two Properties
#ConditionalOnExpression("${properties.first.property.enable:true} && ${properties.second.property.startServer:false}")
Note the following:
You need to using colon notation to indicate the default value of
the property in the expression language statement
Each property is in a separate expression language block ${}
The && operator is used outside of the SpEL blocks
It allows for multiple properties that have differing values and can extend to multiple properties.
If you want to check more then 2 values and still maintain readability, you can use the concatenation operator between different conditions you are evaluating:
Solution for more then 2 properties
#ConditionalOnExpression("${properties.first.property.enable:true} " +
"&& ${properties.second.property.enable:true} " +
"&& ${properties.third.property.enable:true}")
The drawback is that you cannot use a matchIfMissing argument as you would be able to when using the #ConditionalOnProperty annotation so you will have to ensure that the properties are present in the .properties or YAML files for all your profiles/environments or just rely on the default value
Taken from here Spring Boot SpEL ConditionalOnExpression check multiple properties
I am looking for configurationOnProperty usage where I can specify to
consider more than one value
You can use Condition interface of Spring 4.0.
This interface has a method matches(...) which you can use.
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
public class TestCondition implements Condition {
#Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
String testValue = (context.getEnvironment().getProperty("test.configname");
return "value1".equalsIgnoreCase("testValue") || "value2".equalsIgnoreCase("testValue");
}
}
And then use TestCondition inside your #Configuration like below :
#Configuration
public class TestConfig {
#Conditional(value=TestCondition .class)
public MyBean getTestConfigBean() {
//TODO YOUR CODE;
}
}
I would like to know if it is possible to specify
confiugrationOnProperty with condition of havingValue != "value3"
public class TestCondition2 implements Condition {
#Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
String testValue = (context.getEnvironment().getProperty("test.configname");
return ! "value3".equalsIgnoreCase("testValue");
}
}
And then use it like this :
#Configuration
public class TestConfig {
#Conditional(value=TestCondition2 .class)
public MyBean getTestConfigBean() {
//TODO YOUR CODE;
}
}

Autowire Spring Bean based on boolean variable

I want to configure the spring beans in such a way that depending on the value of a boolean variable, one of the two available connection bean gets autowired in the code.
Below is the initialization of the boolean variable:
//This is overridden as false from the properties file on the server.
#Value(value = "${my.property.connectionOne:true}")
private boolean connectionOne;
I have defined the Bean in such a way:
#Bean(name = "specificConnection")
public Destination getSpecificConnection() throws Exception {
if (connectionOne) { //boolean variable
return new ConnectionOne("DB");
}
else {
return new ConnectionTwo("XML");
}
}
where ConnectionOne and ConnectionTwo both implement Destination
And I am using the bean in the desired class as:
#Autowired
#Qualifier(value = "specificConnection")
private Destination specificConnection;
However, it doesn't seem to work. It keeps returning ConnectionOne only even if I change the value of the boolean variable to false.
I am using Spring version 4.2.0 and Wildfly Server.
Please let me know if any further clarification is required.
I want to configure the spring beans in such a way that depending on
the value of a boolean variable
The boolean variable has to be valued before the initialization of the specificConnection bean by Spring.
So what you should probably do is using a value expression.
#Value("${isConnectionOne}") // looks the value in the available placeholder
private boolean isConnectionOne;
#Bean(name = "specificConnection")
public Destination getSpecificConnection() throws Exception {
if (connectionOne) { //boolean variable
return new ConnectionOne("DB");
}
else {
return new ConnectionTwo("XML");
}
}
This is a perfect example for spring profiles! Have a look on this link:
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-profiles.html
In Spring, you can define different profiles your program will run in. Based on settings you define in your application.properties your program will use different beans of these profiles. :)
I hope that could help you!
Greethings
Noixes

spring #Aspect can not work with swagger2

pom.xml version info:
springfox-swagger2: 2.5.0
swagger-core: 1.5.10
springfox-swagger-ui: 2.6.1
springboot: 1.5.3
I has a project with swagger2 and springboot.
The project code without #Aspect works very well.The code reads as follows.
public interface TestApi {
WfExecution test(Long temp);
}
#Api(value = "TestAPI")
#RequestMapping(value = "/test")
#RestController
public class TestApiImpl implements TestApi {
#Override
#RequestMapping(value = "/test")
#ApiOperation(value = "", notes = "", produces = MediaType.APPLICATION_JSON)
public WfExecution test(#ApiParam(value = "", required = true) #RequestParam(required = true, value = "temp")
Long temp) {
return new WfExecution();
}
}
the right result:
But when I add the follow code, the swagger-ui doesn't show the test-api-impl.
#Aspect
#Component
public class LoggerAop {
#Before("execution(* com.XXX.controller.impl.TestApiImpl.*(..))")
public void doBeforeAdvice(JoinPoint joinPoint){
System.out.println("XXX");
}
}
the error result:
Is there a conflict between swagger and spring AOP?
#egg
I setup the similar project and faced a same issue.
After setting the proxyTargetClass property to true in #EnableAspectJAutoProxy annotation as below, the issue got resolved.
#EnableAspectJAutoProxy(proxyTargetClass=true)
This issue occurs only when we are using the interface for controller.
To quote the use of this property EnableAspectJAutoProxy from Java doc.
Users can control the type of proxy that gets created for {#code FooService} using
the {#link #proxyTargetClass()} attribute. The following enables CGLIB-style 'subclass'
proxies as opposed to the default interface-based JDK proxy approach.
This answer is for those who is still facing the issue
#Aspect is basically a filter. If you exclude the swagges resources it will start working.
you can use
#Pointcut("within(com..*..*Controller)")
#Pointcut("within(com..*..*Service)")
so it will scan only those pacakges which is starting from com...

Categories