Spring Expression Language cannot resolve method on class - java-8

I'm trying to evaluate some expression written in SpEL programmatically.
ExpressionParser expressionParser = new SpelExpressionParser();
Expression expression = expressionParser.parseExpression(annotation.filter());
SimpleEvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
context.setVariable("authorities", authentication.getAuthorities());
return expression.getValue(context, Boolean.class);
As you can see, I add a variable, called authorities. I'm trying to evaluate this expression: #authorities.isEmpty().
But I get the exception:
Caused by: org.springframework.expression.spel.SpelEvaluationException: EL1004E: Method call: Method isEmpty() cannot be found on type java.util.ArrayList
at org.springframework.expression.spel.ast.MethodReference.findAccessorForMethod(MethodReference.java:225) ~[spring-expression-5.1.2.RELEASE.jar:5.1.2.RELEASE]
at org.springframework.expression.spel.ast.MethodReference.getValueInternal(MethodReference.java:134) ~[spring-expression-5.1.2.RELEASE.jar:5.1.2.RELEASE]
...
Anyone knows a solution to this problem?

The SimpleEvaluationContext does not allow arbitrary method invocation. See its Javadocs.
If you "trust" the SpEL expression, use
StandardEvaluationContext context = new StandardEvaluationContext();

Related

How to use Kotlin's `is` operator in SpEL expression?

I have a simple sealed class
sealed class Target {
class User(val id: Long) : Target()
class City(val id: String) : Target()
}
that is used as a parameter of s Spring bean method. I'd like to cache the method via the #Cacheable conditionally only when the parameter is User.
#Cacheable(CACHE_NAME, condition = "#target is Target.User")
open fun getFeed(target: Target): Map<String, Any?> { ... }
However I get an error: '(' or <operator> expected, got 'is'
How can I use is in the condition string?
Thanks to Raphael's answer I was able to find out that
Instead of Kotlin's is there's Java's instanceof.
SpEL has a special syntax for using instanceof where you need to use a wrapper around the class: filterObject instanceof T(YourClass).
The fully qualified class name must be used for classes from any other package than java.lang.
The fully qualified name available on runtime for a class defined inside the body of a sealed class is <package>.<SealedClass>$<SubClass>. In my case it was net.goout.feed.model.Target$User.
Putting all this together yeilds this SpEL
#target instanceof T(net.goout.feed.model.Target$User)
As far as I know, SpEL is java-based, and Java does not have an operator called 'is'. The Java equivalent of 'is' is 'instanceof'. Since Java and Kotlin are interoperable and you can work with Kotlin classes in a Java context, #target instanceof FeedTarget.User should work fine.

How to Mock a JDBC datasource Object causing illegal Argument exception saying Property 'data source' is required?

my dao class contains a statement like below
JdbcTemplate jdbcTemplate = new JdbcTemplate(datasourceResolver.selectDataSource(region));
which i m having difficulty to mock the statement
I have created mock objects for datasourceResolver and datasource in my test class and called like below in my test method but its throwing illegal Argument exception saying Property 'datasource' is required'
Mockito.when(datasourceResolver.selectDataSource(Mockito.anyString())).thenReturn(dataSource);
please advise how can we mock this datasource object?
Thanks in Advance!
Did you inject mock?
Put breakpoint to JdbcTemplate jdbcTemplate = new JdbcTemplate(datasourceResolver.selectDataSource(region)); line end check instance of your datasourceResolver.

ConditionalOnExpression fails to compare configuration property to enum type, works as string

I want to load a #Configuration class based on an enum in properties file, so I have the following class:
#Configuration
#ConditionalOnExpression("#{ ${demo.spel.demo-enum} eq T(demo.spel.DemoEnum).VALUE }")
public class DemoConfig {}
And I have: demo.spel.demo-enum=value in application.properties
This does not work, and throws the exception:
Caused by: org.springframework.expression.spel.SpelEvaluationException: EL1008E: Property or field 'value' cannot be found on object of type 'org.springframework.beans.factory.config.BeanExpressionContext' - maybe not public or not valid?
The odd thing is, that if I add single quotes to the property part, and a toString() to the enum part of the expression, there is no exception, the condition is true, and the bean is created (verified by checking console output in debug logging):
#ConditionalOnExpression("#{ '${demo.spel.demo-enum}' eq T(demo.spel.DemoEnum).VALUE.toString() }")
Questions:
Why is comparing an enum like this fails ? How come Spring can successfully convert the values and compare as string but not as their types ?
This is on Spring Boot 2.0.4
It should be pretty obvious, really.
Consider the following Java code:
foo.equals(DemoEnum.VALUE)
It would require an object foo, perhaps a field on this:
this.foo.equals(DemoEnum.VALUE)
If your property placeholder evaluates to 'foo', your first SpEL expression is the equivalent of
#this.foo eq T(DemoEnum).VALUE
So SpEL looks for a property foo on #this
EDIT
If you create a Class:
public class Foo {
#Value("${some.property}")
private DemoEnum enum;
public getEnum() {
return this.enum;
}
}
And add a bean to the context called "foo" you could then use
foo.enum eq ...
Since #this is a BeanExpressionContext allowing you to reference other beans.
I had a similar problem.
I had a feature, which was enabled by default. To disable it, application config file, should have it disabled explicitly. demo.feature.disable:true
I had a spring bean conditional on this property (enabled by default).
#ConditionalOnExpression("#{ ${demo.feature.disable} != true }")
#Component
public class FeatureModule {
}
The problem was, when demo.spel.value was not defined in the config file - application.yml, initialization of this component will fail with
Caused by: org.springframework.expression.spel.SpelParseException: EL1041E: After parsing a valid expression, there is still more data in the expression: 'lcurly({)'
To solve it, I provided default value.
#ConditionalOnExpression("#{ ${demo.feature.disable:false} != true }")
#Component
public class FeatureModule {
}
Now, when I test it.
By default this component is initialized.
If config file does not have demo.feature.disable, this component will be initialized.
If config file has demo.feature.disable:true, this component will not be initialized.
If config file has demo.feature.disable:false this component will be initialized.

how to use Spring-EL in #Value when using constants to resolve a property

I'm trying to use a constant to define a property and then resolving it with the #Value annotation.
I defined the constant in an interface:
public interface InternalConstant{
public static final String JOB_NAME_PROPERTY = "javabatch.jobName";
}
I'm using springboot and I'm adding the property as a default property to the context:
SpringApplication springApp = new SpringApplication(configs.toArray());
Properties props = new Properties();
props.setProperty(InternalConstants.JOB_NAME_PROPERTY, "MyStartableJob");
springApp.setDefaultProperties(props);
Now, I'd like to use #Value to inject the String "MyStartableJob" into a String.
But instead of directly using #Value(value="${javabatch.jobName}), I'd like to use the defined constant.
I tried
#Value(value="#{T(ch.mobi.javabatch.core.internal.InternalConstants).JOB_NAME_PROPERTY}")
But of course, this resolves only to "javabatch.jobName" and not to the value of the property named "javabatch.jobName".
So I tried to wrap it in #Value(value="${#{T(ch.mobi.javabatch.core.internal.InternalConstants).JOB_NAME_PROPERTY}}"), but this causes an exception.
Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder '#{T(ch.mobi.javabatch.core.internal.InternalConstants).JOB_NAME_PROPERTY}' in string value "${#{T(ch.mobi.javabatch.core.internal.InternalConstants).JOB_NAME_PROPERTY}}"
at org.springframework.util.PropertyPlaceholderHelper.parseStringValue(PropertyPlaceholderHelper.java:174)
at org.springframework.util.PropertyPlaceholderHelper.replacePlaceholders(PropertyPlaceholderHelper.java:126)
at org.springframework.core.env.AbstractPropertyResolver.doResolvePlaceholders(AbstractPropertyResolver.java:204)
at org.springframework.core.env.AbstractPropertyResolver.resolveRequiredPlaceholders(AbstractPropertyResolver.java:178)
at org.springframework.context.support.PropertySourcesPlaceholderConfigurer$2.resolveStringValue(PropertySourcesPlaceholderConfigurer.java:175)
at org.springframework.beans.factory.support.AbstractBeanFactory.resolveEmbeddedValue(AbstractBeanFactory.java:801)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:955)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:942)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:533)
... 16 more
I know, that I could simply inject the Environment and using its getProperty method:
#Autowired
private Environment env;
public void m1() {
env.getProperty(InternalConstants.JOB_NAME_PROPERTY);
}
This works and serves my purpose.
But I wonder, if this could also be done using SPEL in #Value.
Thanks.
What about the simplest approach:
#Value("${" + InternalConstant.JOB_NAME_PROPERTY + "}")
private String jobName
You can access the property referenced by the constant using environment directly in the SpEL expression and the correct value should be injected:
#Value("#{environment.getProperty(T(com.example.InternalConstants).JOB_NAME_PROPERTY)}")
private String jobName;

Define Spring bean with a Map-typed constructor argument

In my Grails app, I'm trying to define a Spring bean in resources.groovy that requires a Map-typed constructor arg. I tried this:
Map<Class, String> mapArg = [(String): 'foo']
myBean(MyBeanImpl, mapArg)
But I get the error message:
org.springframework.beans.factory.BeanCreationException: Error
creating bean with name 'myBean': Could not resolve matching
constructor (hint: specify index/type/name arguments for simple
parameters to avoid type ambiguities)
The implementation class has a single constructor which is defined thus
MyBeanImpl(Map<Class, String> map) {
// impl omitted
}
My guess is that the problem is caused by the fact that I've defined a constructor that takes a single Map arg which has the same signature as the default constructor Groovy adds to every class.
If so, a solution would appear to be to add a factory method such as
MyBean getInstance(Map map) {
// impl omitted
}
But I'm not sure how I can call this to define a bean (in resources.groovy) that is constructed from a factory method that requires a parameter.
As far as I can tell the syntax you're using ought to work. Does the alternative syntax:
Map<Class, String> mapArg = [(String): 'foo']
myBean(MyBeanImpl) { bean ->
bean.constructorArgs = [mapArg]
}
work any better in your case? Failing that, declaring the map as a bean in its own right should definitely do it:
import org.springframework.beans.factory.config.MapFactoryBean
classMap(MapFactoryBean) {
sourceMap = [(String):'foo']
}
myBean(MyBeanImpl, classMap /* this is a RuntimeBeanReference */)

Resources