Spring | SPEL multiple property accessors - spring

I'm trying to use SPeL with multiple property accessors.
StandardEvaluationContext simpleContext = new StandardEvaluationContext(myPojo);
simpleContext.setVariable("ctx", ruleExecutionContext);
simpleContext.setPropertyAccessors(Arrays.asList(new MapAccessor(), new ReflectivePropertyAccessor()));
ExpressionParser parser = new SpelExpressionParser();
return (Boolean) parser.parseExpression(spelExpression).getValue(simpleContext, RulebaseConfiguration.LIB_MAP);
RulebaseConfiguration.LIB_MAP contains {"instanceName": instance}
I want to pass expressions that could operate on a POJO as well as call methods on the instance. But it only takes map into the effect.
I get this error:
SEVERE: Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.expression.spel.SpelEvaluationException: EL1008E: Property or field 'payload' cannot be found on object of type 'java.util.HashMap' - maybe not public?] with root cause
org.springframework.expression.spel.SpelEvaluationException: EL1008E: Property or field 'payload' cannot be found on object of type 'java.util.HashMap' - maybe not public?
at org.springframework.expression.spel.ast.PropertyOrFieldReference.readProperty(PropertyOrFieldReference.java:226)

Creating a context and parsing the expression for each request is wasteful, unless it's different for each request; in which case, consider caching expressions/contexts.
As I said, since you are passing a rootObject to getValue(), your myPojo is "hidden" - the evaluation is always performed on LIB_MAP.
You need to call getValue() without a root object to use the context's root object. You can add LIB_MAP as a variable (e.g. with name nationalityLookup) and use
payload['channel'] == #nationalityLookup.resolveChannel('CBR1000')

Related

Error in spring mongodb for indexing a hashmap

I created in a pojo a index as it follows for the properties "attributes" that is a Map class
#CompoundIndex(name = "attributes", def = "{'attributes.key' : 1,attributes.value:1}")
i wish index the keys and the values of that map . I read in a another stackoverflow that is possibile to create a index in a nested properties but i have this error
" Failed to instantiate [org.springframework.data.mongodb.core.MongoTemplate]: Factory method 'mongoTemplate' threw exception; nested exception is org.bson.json.JsonParseException: Invalid JSON input. Position: 25. Character: '.'."

Parse LocalTime string inside the #Value attribute

My environment variable is a string with time 23:30 In my String Boot application I'd like to parse it automatically and set it the result to variable.
I tried like this
#Value("#{ java.time.LocalTime.parse('${NIGHT_START_TIME}')}")
private LocalTime NIGHT_START_TIME;
IntelliJ shows the error
Cannot resolve variable 'java'
Here is log
Unsatisfied dependency expressed through field 'NIGHT_START_TIME';
nested exception is org.springframework.beans.factory.BeanExpressionException: Expression parsing failed; nested exception is org.springframework.expression.spel.SpelEvaluationException: EL1008E: Property or field 'LocalTime' cannot be found on object of type 'org.springframework.beans.factory.config.BeanExpressionContext' - maybe not public?
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:588)
If I read variable like this I see that value is correct
#Value("${NIGHT_START_TIME}")
private String NIGHT_START_TIME;
Any ideas how to make it work?
Try this
#Value("#{ T(java.time.LocalTime).parse('${NIGHT_START_TIME}')}")
private LocalTime NIGHT_START_TIME;
The way you reference a class is using T.
Check the documentation.
Have checked at Spring Boot 2.2.6.RELEASE:
application.yml
NIGHT_START_TIME: '23:30'
Service
#Value("${NIGHT_START_TIME}")
private LocalTime NIGHT_START_TIME;
Work perfect without any "knee bends":)

#Value annotation for reading Map from property file

My Java has this
#Value("#{${validators}}")
private Map<String,String> validators;
property file has this
validators={AlphabetValidator:'AlphabetValidator',NumberValidator:'NumberValidator'AlphaNumericValidator:'AlphaNumericValidator',DateValidator:'DateValidator', FixedLengthValidator:'FixedLengthValidator',MinimumLengthValidator:'MinimumLengthValidator',MaximumLengthValidator:'MaximumLengthValidator',CustomValidator:'CustomValidator' MandatoryFieldValidator: 'MandatoryFieldValidator',TimeValidator:'TimeValidator'}
when I run the app.. I am getting this error
Exception in thread "main"
org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'validatorProperties': Unsatisfied
dependency expressed through field 'validators'; nested exception is
org.springframework.beans.factory.BeanExpressionException: Expression
parsing failed; nested exception is
org.springframework.expression.spel.SpelParseException: EL1041E:(pos
1): After parsing a valid expression, there is still more data in the
expression: 'lcurly({)'
Check out for comma ',' in property file.
Properties file:
validators={AlphabetValidator:'AlphabetValidator',NumberValidator:'NumberValidator',AlphaNumericValidator:'AlphaNumericValidator',DateValidator:'DateValidator',FixedLengthValidator:'FixedLengthValidator',MinimumLengthValidator:'MinimumLengthValidator',MaximumLengthValidator:'MaximumLengthValidator',CustomValidator:'CustomValidator',MandatoryFieldValidator:'MandatoryFieldValidator',TimeValidator:'TimeValidator'}
Java code:
#Value("#{${validators}}")
private Map<String,String> validators;
You have missed to specify some commas inside your properties. Try with following:
validators={AlphabetValidator:'AlphabetValidator',NumberValidator:'NumberValidator',AlphaNumericValidator:'AlphaNumericValidator',DateValidator:'DateValidator',FixedLengthValidator:'FixedLengthValidator',MinimumLengthValidator:'MinimumLengthValidator',MaximumLengthValidator:'MaximumLengthValidator',CustomValidator:'CustomValidator',MandatoryFieldValidator:'MandatoryFieldValidator',TimeValidator:'TimeValidator'}

spring aop - exception while creating advice around JdbcTemplate methods

I've a web application that uses apache dbcp and spring jdbc to perform database operations on an oracle database. I need to write a performance logger that logs the individual times of each database operation. I tried writing an around advice on all 'execute' methods of org.springframework.jdbc.core.JdbcTemplate but it results in an error when spring gets initialized. The logger class and the exception stacktrace is as follows:-
I also tried to use CGLIB proxies by enabling but it errors out on dao classes that extends from spring's StoredProcedure class and use constructor injection.
#Aspect
public class Logger {
#Around("this(org.springframework.jdbc.core.JdbcTemplate) && execution(* execute(*))")
public Object invoke(ProceedingJoinPoint pjp) throws Throwable {
long time = System.currentTimeMillis();
Object result = pjp.proceed();
LOGGER.debug("time consumed = " + (System.currentTimeMillis() - time));
return result;
}
Exception stacktrace:
SEVERE: Exception sending context initialized event to listener instance of class org.springframework.web.context.ContextLoaderListener
org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'myDao' defined in class path resource [spring/my-dao.xml]: Unsatisfied dependency expressed through constructor argument with index 0 of type [org.springframework.jdbc.core.JdbcTemplate]:
Could not convert constructor argument value of type [$Proxy7] to required type [org.springframework.jdbc.core.JdbcTemplate]:
Failed to convert value of type '$Proxy7 implementing org.springframework.jdbc.core.JdbcOperations,org.springframework.beans.factory.InitializingBean,org.springframework.aop.SpringProxy,org.springframework.aop.framework.Advised'
to required type 'org.springframework.jdbc.core.JdbcTemplate';
nested exception is
java.lang.IllegalStateException: Cannot convert value of type [$Proxy7 implementing org.springframework.jdbc.core.JdbcOperations,org.springframework.beans.factory.InitializingBean,org.springframework.aop.SpringProxy,org.springframework.aop.framework.Advised]
to required type [org.springframework.jdbc.core.JdbcTemplate]:
no matching editors or conversion strategy found
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:702)
at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:196)

Spring binding exception when a form is submitted

Stuck and have no clue why Spring Form is not able to submit successfully [gives binding issue] when pre-populated in get Request call loadForm, but works fine when populated in a method setupFormObject with #ModelAttribute annotation tag. I can provide a simple example in github to test out if asked for :)
Example below
#ModelAttribute("showForm")
public ShowForm setupFormObject() {
//Instantiate showForm with data
return showForm;
}
#RequestMapping(method = RequestMethod.GET)
public ModelAndView loadForm(#RequestParam("id") String id, HttpSession session) {
ModelAndView modelAndView = new ModelAndView(nextPage);
//Instantiate showForm with data
//modelAndView.addObject("showForm", showForm);
return modelAndView;
}
#RequestMapping(method = RequestMethod.POST)
public String post(#ModelAttribute("showForm") ShowForm showForm, BindingResult result, final RedirectAttributes redirectAttrs) {
//I see changed data here in showForm when populated using #setupFormObject
//See an exception in JSP with binding error if populated in loadForm
return "";
}
Stack Trace as requested. This exception is from github example.
`HTTP Status 500 - Request processing failed; nested exception is org.springframework.beans.InvalidPropertyException: Invalid property 'users[0]' of bean class [com.example.UserForm]: Illegal attempt to get property 'users' threw exception; nested exception is org.springframework.beans.NullValueInNestedPathException: Invalid property 'users' of bean class [com.example.UserForm]: Could not instantiate property type [com.example.UserEntity] to auto-grow nested property path: java.lang.InstantiationException: com.example.UserEntity`
`type Exception report`
`message Request processing failed; nested exception is org.springframework.beans.InvalidPropertyException: Invalid property 'users[0]' of bean class [com.example.UserForm]: Illegal attempt to get property 'users' threw exception; nested exception is org.springframework.beans.NullValueInNestedPathException: Invalid property 'users' of bean class [com.example.UserForm]: Could not instantiate property type [com.example.UserEntity] to auto-grow nested property path: java.lang.InstantiationException: com.example.UserEntity`
`description The server encountered an internal error that prevented it from fulfilling this request.`
`exception`
`org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.springframework.beans.InvalidPropertyException: Invalid property 'users[0]' of bean class [com.example.UserForm]: Illegal attempt to get property 'users' threw exception; nested exception is org.springframework.beans.NullValueInNestedPathException: Invalid property 'users' of bean class [com.example.UserForm]: Could not instantiate property type [com.example.UserEntity] to auto-grow nested property path: java.lang.InstantiationException: com.example.UserEntity`
`org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:927)
org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:822)
javax.servlet.http.HttpServlet.service(HttpServlet.java:647)
org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:796)
javax.servlet.http.HttpServlet.service(HttpServlet.java:728)`
`root cause`
`org.springframework.beans.InvalidPropertyException: Invalid property 'users[0]' of bean class [com.example.UserForm]: Illegal attempt to get property 'users' threw exception; nested exception is org.springframework.beans.NullValueInNestedPathException: Invalid property 'users' of bean class [com.example.UserForm]: Could not instantiate property type [com.example.UserEntity] to auto-grow nested property path: java.lang.InstantiationException: com.example.UserEntity
org.springframework.beans.BeanWrapperImpl.getPropertyValue(BeanWrapperImpl.java:829)
org.springframework.beans.BeanWrapperImpl.getNestedBeanWrapper(BeanWrapperImpl.java:556)
org.springframework.beans.BeanWrapperImpl.getBeanWrapperForPropertyPath(BeanWrapperImpl.java:533)
org.springframework.beans.BeanWrapperImpl.setPropertyValue(BeanWrapperImpl.java:894)
org.springframework.beans.AbstractPropertyAccessor.setPropertyValues(AbstractPropertyAccessor.java:75)
org.springframework.validation.DataBinder.applyPropertyValues(DataBinder.java:699)
org.springframework.validation.DataBinder.doBind(DataBinder.java:595)
org.springframework.web.bind.WebDataBinder.doBind(WebDataBinder.java:191)
org.springframework.web.bind.ServletRequestDataBinder.bind(ServletRequestDataBinder.java:112)
org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor.bindRequestParameters(ServletModelAttributeMethodProcessor.java:153)
org.springframework.web.method.annotation.ModelAttributeMethodProcessor.resolveArgument(ModelAttributeMethodProcessor.java:106)
org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:77)
org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:162)
org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:123)
org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104)
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:746)
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:687)
org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:80)
org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:925)
org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:856)
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:915)
org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:822)
javax.servlet.http.HttpServlet.service(HttpServlet.java:647)
org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:796)
javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
`
Your help is highly appreciated
Thanks
The issue is actually that UserEntity does not have a default constructor, if you add the constructor it will work cleanly:
public UserEntity(){
//
}
I think what's happening is that the way your form is declared in the JSP, when submitting it it will assume that there is a UserEntity instance at position 0 in the users List of the UserForm instance, but it actually stumbles over a null reference.
Yes, you do add some UserEntity instances to the model when the form is displayed (using the loadForm method which is mapped to HTTP Get), but when you submit the form, a new model object is created, and this one will only have nulls in the users List.
Try making the model session scoped, this way the model instance you create when the form is shown will be reused when the form is submitted. Or create your own implementation of List that will return a new instance of UserEntity when a get(int position) is performed and there's nothing there.

Resources