Spring MVC custom converter not working - java

I am trying to create a custom Converter for my Spring MVC application but it is never triggered.
Here's how I implemented it and registered it :
public class ObjectIdSpringConverter implements Converter<String, ObjectId>{
public ObjectIdSpringConverter(){
System.out.println("ObjectIdSpringConverter picked up ");
}
#Override
public ObjectId convert(String source) {
System.out.println("ObjectIdSpringConverter converting");
return new ObjectId(source);
}
}
And in my Spring MVC configuration I register it as follow :
#Configuration
#ComponentScan(basePackages = {"myapp.web.controller"})
public class SpringMvcConf extends WebMvcConfigurationSupport{
#Override
protected void addFormatters(FormatterRegistry registry) {
registry.addConverter(new ObjectIdSpringConverter());
}
//other stuff here
}
I placed some breakpoints in the converter's constructor and convert() method, and I can see that it is properly constructed, however the convert() method is never called which results in icorrect values received by my controller.
Here's an example controller method :
#Controller
public class HomeController {
#RequestMapping(value="/home/testObjectId.amlgm", method = RequestMethod.GET)
public ObjectId testObjectId(ObjectId oid){
System.out.println("expected 552952cec9ac88712ee0d36b, actual " + oid.toString());
System.out.println(oid);
return oid;
}
}
I know the convert method is never called because the break point is never hit and because the value is not the expected one. I also debugged / traced what spring was doing and it does not seem to use my converter to instantiate the parameter ObjectId of the controller method.
Can anyone point me to what I am missing ?
Thanks

Your formatter should implement Formatter<T>, where T will be ObjectId in your case
http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/format/FormatterRegistry.html#addFormatter-org.springframework.format.Formatter-

Related

How to add an Interface into a constructor in an ItemProcessor with SpringBatch

In my java project I use the hexagonal architecture.
I have an Interface "Use Case" called RapprochementUseCase who is implemented by a Service called "RapprochementService".
In my ItemProcessor of my spring batch step I need to call to my Interface "RapprochementUseCase", so in my Processor I put in my constructor the interface thank's to the annotation RequiredArgsConstructor. But I got an error when I try to put the Interface into the parameter of my Processor.
I don't see in the documentation how to do this.. Do you have any ideas ?
In my declaration of processor :
#Bean
public ItemProcessor<PlaqueSousSurveillanceEntity, PlaqueLueEntity> rapprochementProcessor() {
return new RapprochementProcessor(); <-- Error here
}
RapprochementProcessor :
#Slf4j
#RequiredArgsConstructor
public class RapprochementProcessor implements ItemProcessor<PlaqueSousSurveillanceEntity, PlaqueLueEntity> {
private final RapprochementUseCase rapprochementUseCase;
#Override
public PlaqueLueEntity process(PlaqueSousSurveillanceEntity item) {
log.trace("Traitement d'une entrée PlaqueSousSurveillanceEntity: {}", item);
List<PlaqueLue> plaqueLues = this.rapprochementUseCase.findRapprochementByPlaque(item.getPlaque());
return new PlaqueLueEntity();
}
}
When I tried to put the RapprochementUseCase in the contructor of the BatchConfiguration and if I declare the bean like :
#Bean
public RapprochementUseCase rapprochementUseCase(RapprochementUseCase rapprochementUseCase) {
return rapprochementUseCase;
}
I got an error : The dependencies of some of the beans in the application context form a cycle:
Your RapprochementProcessor requires a RapprochementUseCase, you should have a constructor generated by #RequiredArgsConstructor.
You need to declare a bean of type RapprochementUseCase, and then pass it to your item processor like follows for example:
#Bean
public ItemProcessor<PlaqueSousSurveillanceEntity, PlaqueLueEntity> rapprochementProcessor(RapprochementUseCase rapprochementUseCase) {
return new RapprochementProcessor(rapprochementUseCase);
}

How to audit methods in Java Spring Boot

I am writing a Spring Boot Application. I want to audit methods with my annotation #AuditMetod: For example I have method foo() with the annotation:
#AuditMetod(name = "SomeValue")
foo() {...}
I want to handle and audit such methods like this (the simplest example):
auditMethod(Method method) {
if (method.hasAnnotation(AuditMethod.class)) {
System.out.println (method.getName() + " was called at " + new Date())
}
}
upd
Thanks to #Karthikeyan #Swapnil Khante and #misha2048 I understood, that I need to use AOP. But I have 2 problems:
The only method in Aspect class in not being called and I don't see the inscription "----------ASPECT METHOD IS CALLED-----------" in log
How can I check in aspect method what method it is intercepting. To get an instance of Method class.
Now I have the following code:
Controller:
#PostMapping
#LoggingRest(executor = "USER", method = "CREATE", model = "SUBSCRIPTION")
public ResponseEntity<?> create(#Valid #RequestBody SubscriptionRequestDto dto) {
...
}
Aspect:
`#Aspect
#Slf4j
#Component
public class AuditAspect {
#Pointcut(value = "#annotation(com.aspect.annotations.LoggingRest)")
public void auditMethod(ProceedingJoinPoint proceedingJoinPoint) {
log.info("----------ASPECT METHOD IS CALLED------------");
}`
And annotation:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface LoggingRest {
String executor() default "SYSTEM";
String method() default "";
String model() default "";
}
Auditing is a cross-cutting concern and can be handled using AOP.
Another solution would be to use a low-level solution by writing a custom annotation and using a Spring interceptorto write your business logic.
To use the Spring interceptor you will need to implement the HandlerInterceptor interface
Example of the annotation
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface Audit {
boolean active() default true;
}
Interceptor example
#Component
public class AuditInterceptor implements HandlerInterceptor {
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
Audit annotation = handlerMethod.getMethodAnnotation(Audit.class);
if (annotation != null && annotation.active()) {
// your business logic
}
}
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
check this interceptor example
I think one of the solutions here, as #Karthikeyan mentioned, is to use Spring AOP.
If you are not aware a brief introduction - spring-aop module implements the aspect oriented programming paradigm. We extract some common functionality, that we generally want to apply to some subset of functions/methods, to an entity called Aspect (see class annotated with #Aspect). This class will contain out cross-cutting functionality - such as auditing, for instance we want to audit the methods execution time, lets say. We just put the code to be executed, the condition, which tell the spring what exact beans methods should be affect by this aspect, see below.
For example, if I can audit the method execution duration with the following very simple example (in my case I said that any public method, returning void inside the Class com.example.stackoverflow.BusinessLogicClass must be inspected by this Aspect):
#SpringBootApplication
#EnableAspectJAutoProxy
public class StackoverflowApplication implements ApplicationRunner {
#Autowired
private BusinessLogicClass businessLogicClass;
public static void main(String[] args) {
SpringApplication.run(StackoverflowApplication.class, args);
}
#Override
public void run(ApplicationArguments args) throws Exception {
businessLogicClass.test();
}
}
#Aspect
#Component
class MyAspectLogicClass {
#Around("execution(public void com.example.stackoverflow.BusinessLogicClass.*(..))")
public Object hangAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
long before = System.currentTimeMillis();
Object returnedValue = proceedingJoinPoint.proceed();
long after = System.currentTimeMillis();
System.out.printf("Retruned in '%s' ms %n", (after - before));
return returnedValue;
}
}
#Component
class BusinessLogicClass {
public void test() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
In my case, I will get the time before method execution, then by the means of
proceedingJoinPoint.proceed() call I delegate the execution to the real method, and then, once I get the response back, I will get the current system time and calculate the execution time, fairly simple.
I hope I have at least directed you somewhere, if you are looking for documentation, this are the resources I suggest you should look for:
https://docs.spring.io/spring-framework/docs/2.5.x/reference/aop.html offical spring doc (stale a bit, but there are some valuable things to learn)
https://docs.spring.io/spring-framework/docs/4.3.15.RELEASE/spring-framework-reference/html/aop.html is more fresh doc
Hope it helped :)
The problem was in right annotation. In Aspect class I tried #Around and everything works as I need.
#Aspect
#Slf4j
#Component
public class AuditAspect {
#Around(value = "#annotation(com.aspect.annotations.LoggingRest)")
public void auditMethod(ProceedingJoinPoint proceedingJoinPoint) {
var method = ((MethodSignature) proceedingJoinPoint.getSignature()).getMethod();
log.info("----------ASPECT METHOD IS CALLED------------");
}
}
For getting a Method instance I use fallowing code
Method method = ((MethodSignature) proceedingJoinPoint.getSignature()).getMethod();

Intercept the instance of an object from RestController or Hibernate in Spring before it's returned to the client

I'm developing a translation service that currently works inside another Service. For example:
public Profile getById(int chainId, int profileId, Integer languageId) {
Profile profile = profileRepository.getById(chainId, profileId);
translationService.translate(profile, languageId); // Here
return profile;
}
Now, to avoid to use a translate method on every service method of all the application, and as I only have the language of a user from the controller, I would like to execute the translate method before every Profile (and any other object) is returned to the client.
I tried to implement HandlerInterceptor in a custom interceptor, but it seems it doesn't returns the instance of the object that I'm returning. Anyone could help?
Another way to do it could be to translate every object that came from a select in Hibernate, but I also don't find any good solution to it this way...
The solution was to use Spring AOP. Probably the question wasn't very well explained, but what we needed was a way to intercept the object a user was asking to the backend, because they are able to create their own translations and we save them in the database. We had to return the model with the correct translation for each user, who has their localization in their profile. Here's the way we intercept it:
#Component
#Aspect
public class TranslatorInterceptor extends AccessApiController {
Logger logger = LoggerFactory.getLogger(this.getClass());
#Autowired
public TranslationService translationService;
#Pointcut("execution(* com.company.project.api.controller.*.get*(..))")
public void petitionsStartWithGet() { }
#Pointcut("execution(* com.company.project.api.controller.*.list*(..))")
public void petitionsStartWithList() { }
#Pointcut("execution(* com.company.project.api.controller.*.find*(..))")
public void petitionsStartWithFind() { }
#AfterReturning(pointcut = "petitionsStartWithGet() || petitionsStartWithList() || petitionsStartWithFind()", returning = "result")
public void getNameAdvice(JoinPoint joinPoint, Object result){
translationService.translate(result, getCustomUserDetails().getLanguageId());
logger.debug("Translating " + result.getClass().toString());
}
}
What we do here is to "watch" all the methods in the package "controller" that start by 'get', 'list' or 'find' (getById(), for example) and through this advice, we intercept the object before is sent to Jackson. The method getCustomUserDetails comes from AccessApiController, which is a class we did to provide our Controllers with some information we need.

Spring MVC Missing matrix variable

I'm trying to add a matrix parameter (or matrix variable) to my Rest Controller using SpringMVC (from Spring boot 1.2.3.RELEASE)
Here is my code :
#RestController
public class SubAgentsController {
#RequestMapping(value = "/{subagents}", method = RequestMethod.GET)
public SubAgent subAgents(#MatrixVariable(value="agentName", pathVar="subagents") String agentName) {
System.out.println(agentName);
}
}
Unfortunately, when I try to get :
http://localhost:8080/subagents;agentName=hello
that is the answer I receive :
There was an unexpected error (type=Bad Request, status=400).
Missing matrix variable 'agentName' for method parameter of type String
What did I do wrong ? According to http://docs.spring.io/spring-framework/docs/3.2.0.M2/reference/html/mvc.html that should work :-(
Thanks for your answers!
In SpringBoot Application In order to enable Matrix variables you need to define below override code
#Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void configurePathMatch(PathMatchConfigurer configurer) {
UrlPathHelper urlPathHelper = new UrlPathHelper();
urlPathHelper.setRemoveSemicolonContent(false);
configurer.setUrlPathHelper(urlPathHelper);
}
}
Otherwise, they’re disabled by default
As the documentation you linked to states,
Note that to enable the use of matrix variables, you must set the
removeSemicolonContent property of RequestMappingHandlerMapping to
false. By default it is set to true with the exception of the MVC
namespace and the MVC Java config both of which automatically enable
the use of matrix variables.
If you're configuring your application by extending WebMvcConfigurationSupport, then override the requestMappingHandlerMapping method which prepares the RequestMappingHandlerMapping and set its appropriate property.
#Override
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
final RequestMappingHandlerMapping requestMappingHandlerMapping = super.requestMappingHandlerMapping();
requestMappingHandlerMapping.setRemoveSemicolonContent(false); // <<< this
return requestMappingHandlerMapping;
}
You'll then be all set.
With Spring Boot, I think all you need is to declare a #Bean method with the above, ie. that returns a RequestMappingHandlerMapping instance.
If you are using Spring Data and its controller mapping, try something like this also
#Configuration
public class DataMvcConfig extends RepositoryRestMvcConfiguration {
#Override
public DelegatingHandlerMapping restHandlerMapping() {
final DelegatingHandlerMapping delegatingHandlerMapping = super.restHandlerMapping();
for (HandlerMapping delegate : delegatingHandlerMapping.getDelegates()) {
if (delegate instanceof AbstractHandlerMapping) {
((AbstractHandlerMapping)delegate).setRemoveSemicolonContent(false);
}
}
return delegatingHandlerMapping;
}
}

extending spring mvc controller without using adapter pattern

I have one controller which has 4 request mapping and I cannot do changes in this controller. Is it possible to have a class(controller/component) which overrides one of the request mapping methods to have my custom implementation for this request mapping and using the other 3 request mapping methods as is. Is it possible?
Below is a sample code:
#Controller
public class Base {
#RequestMapping("/add")
public String add() throws Exception {
return "add";
}
#RequestMapping("/update")
public String update() throws Exception {
return "update";
}
#RequestMapping("/remove")
public String remove() throws Exception {
return "remove";
}
#RequestMapping("/show")
public String show() throws Exception {
return "show";
}
}
public class ExtendedBase extends Base {
public String add() throws Exception {
return "newAdd";
}
}
Since you want to override just one method from the parent controller and retain the URLs for all the controller methods, you need a way to prevent Spring from mapping URLs to the parent controller since otherwise you will get duplicate URL mappings when you add the child controller.
Assuming your controllers are:
package com.theirs.web.controller;
#Controller
#RequestMapping("/base")
public class Base {
#RequestMapping("/add")
public String add() { ... }
#RequestMapping("/update")
public String update() { ... }
#RequestMapping("/remove")
public String remove() { ... }
#RequestMapping("/show")
public String show() { ... }
}
package com.mine.web.controller;
#Controller
#RequestMapping("/base")
public class ExtendedBase extends Base {
#Override
#RequestMapping("/add")
public String add() { ... }
}
You could try this in dispatcher-servlet.xml:
<context:component-scan base-package="com.theirs.web,com.mine.web">
<context:exclude-filter type="regex" expression="com\.theirs\.web\.controller\.Base" />
</context:component-scan>
This will exclude the parent controller class from being managed by Spring while allowing the child class to be managed by Spring. Thus, there will be only one class mapped to /base and its child URLs.
It is possible only in case if you provide different mapping for the child controller. Otherwise Spring will complain about ambiguous mapping (as you will have two conrollers with the same mapping).
The solution would be to have different mapping for your child controller.
#Controller
#RequestMapping(value = "/child")
public class ExtendedBase extends Base {
public String add() throws Exception {
return "newAdd";
}
}
Note here, that you should annotate child class with #Controller, otherwise it wouldn't recognize it.
So in this case, you can access your add() via both - child and parent controllers. e.g.
/add - will result calling the Base.add()
/show - will result calling the Base.show()
/child/add - will result calling the ExtendedBase.add()
/child/show - will result calling the Base.show()

Categories