Spring ControllerLoggingAspect doesnt intercept the method - java

I have the following:
#Pointcut("execution(*com.shop.controller.OrderController.save(..))")
public void savingOrder() {
log.info("Saving order details");
}
#Before("savingOrder() && args(..,request)")
public void logSavingOrder(JoinPoint joinPoint, HttpServletRequest request){
log.info("Saving");
}
However, the log never gets displayed.
When I try the following:
#Pointcut("execution(* com.shop.controller.OrderController.saveOrder(..))")
public void savingOrder() {
log.info("Saving order details");/**/
}
#Before("savingOrder()")
public void logSavingOrder(JoinPoint joinPoint){
log.info("Saving order");
}
Everything works. However, I need to have access to the HttpServletRequest.
Any ideas on what I am doing wrong?

Do not add it in the method signature of intercepting method, access it inside the intercepting method using one of the following methods:
If the bean is request scoped you can autowire the HttpServletRequest like:
#Component
#Scope("request")
public class Foo {
#Autowired private HttpServletRequest request;
//
}
Otherwise you can get the current request as follows:
ServletRequestAttributes sra = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
HttpServletRequest req = sra.getRequest();
This uses thread-local under the covers.
If you are using Spring MVC that's all you need. If you are not using Spring MVC then you will need to register a RequestContextListener or RequestContextFilter in your web.xml.

Related

Invoke Same Method for Multiple Services in Application

My application consists of multiple services.We had a requirement now that for every request coming in to our application we need to validate the token.
Current architecture of my application is such that every microservice has its own ServiceInterceptor class and in that class I am writing the logic in prehandle method to validate token recieved in request.
Service Interceptor Class.
#Component
public class ServiceInterceptor implements HandlerInterceptor {
private static final ApplicationLogger logger = ApplicationLogger.getInstance();
#Autowired
TokenInfoServiceImpl tokenInfoServiceImpl;
#Override
#CrossOrigin(origins = "*", maxAge = 3600)
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
String requestPath = request.getRequestURL().toString();
String authToken = request.getHeader("authToken");
String bearerToken = request.getHeader("Authorization");
String userId = request.getHeader("userId");
if (deviceId.equals("web")) {
if (bearerToken.startsWith("Bearer ")){
bearerToken = bearerToken.substring(7, bearerToken.length());
} else {
response.sendError(400, "Expected bearer prefix to Authorization header value.");
}
boolean isTokenValid = tokenInfoServiceImpl.validateToken(bearerToken);
return isTokenValid;
}
return true;
}
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("Post Handle method is Calling");
}
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
Exception exception) throws Exception {
System.out.println("Request and Response is completed");
}
}
My concern is since we have different services,every service has its interceptor class , so in every service will I have to create method validateToken(to validate the token when a request comes to that service) which is obviously not at all an ideal approach.
Is there a way that I could write validateToken method in one place and that could be accessed by all the services(like UserService,PaymentService,etc..) or rather one Interceptor could be used to intercept request for all the individual microservices instead of having separate interceptor for each service .
I know this can be done using API Gateway but right now our team want a quick solution to this .API Gateway will implement later.
If I understand your question & comments you can try below :
Create Configuration bean which implements WebMvcConfigurer
Use your ServiceInterceptor inside addInteceptor & mention endpoints or root context if all endpoints needed this config :
#Configuration
public class ConfigClass implements WebMvcConfigurer{
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new ServiceInterceptor ()).addPathPatterns("/contextroot/**");
}
}
Also you may directly use your ServiceInterceptor without annotating it with Component.

Java interceptor not getting called

I have a Spring boot application and I am implementing and interceptor in order to log some data.
The problem is that is not getting called, I have tried:
#Interceptor
public class LoggerInterceptor{
#AroundInvoke
public Object collectBasicLoggingInformation(InvocationContext context) throws Exception {
Logger logger = LoggerFactory.getLogger(context.getClass());
logger.info("Method Called: " + context.getMethod()
.getName());
logger.info("Parameters: " + Arrays.toString(context.getParameters()));
return context.proceed();
}
}
And then I've applied to methods or classes and in both of them doesn't work:
#GetMapping
#Interceptors(LoggerInterceptor.class)
public List getAllFilingNumber(){
logger.info("This is a test");
return filingNumberService.findAll();
}
Or
#RestController
#RequestMapping(FilingNumberController.BASE_URL)
#Interceptors(LoggerInterceptor.class)
public class FilingNumberController{
#GetMapping
public List getAllFilingNumber(){
logger.info("This is a test");
return filingNumberService.findAll();
}
}
Does someone knows what I am doing wrong?
Thanks
If you are having a springboot application in order to intercept the request to a controller , you have to take a different approach altogethor.
Interceptors are used in conjunction with Java EE managed classes to
allow developers to invoke interceptor methods on an associated target
class, in conjunction with method invocations or lifecycle events.
Common uses of interceptors are logging, auditing, and profiling.
Reference Doc
You are trying to use Java EE annotation with spring , which won't work.In spring-boot you will have to register the interceptors like :
#Configuration
public class WebConfig implements WebMvcConfigurer {
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LocaleChangeInterceptor());
registry.addInterceptor(new ThemeChangeInterceptor()).addPathPatterns("/**").excludePathPatterns("/admin/**");
registry.addInterceptor(new SecurityInterceptor()).addPathPatterns("/secure/*");
}
}
The interceptor itself have to be a class which extends the HandlerInterceptorAdapter and implements the methods as follows.
From Spring DOCS :
All HandlerMapping implementations support handler interceptors that
are useful when you want to apply specific functionality to certain
requests — for example, checking for a principal. Interceptors must
implement HandlerInterceptor from the org.springframework.web.servlet
package with three methods that should provide enough flexibility to
do all kinds of pre-processing and post-processing:
preHandle(..): Before the actual handler is executed
postHandle(..): After the handler is executed
afterCompletion(..): After the complete request has finished
#Component
public class RequestInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object object) throws Exception {
System.out.println("we are Intercepting the Request");
return true;
}
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response,
Object object, ModelAndView model)
throws Exception {
System.out.println("request processing "
+ "completed by #RestController");
}
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object object, Exception arg3)
throws Exception {
System.out.println("afterCompletion Request Completed");
}
}

How to inject spring bean into ContainerRequestFilter using AutoWired?

I'm using RESTEasy 3 and Spring 4 and I'm trying to inject #Autowired an service bean into my interceptor as follow below:
But running this code it's returning Null Pointer Exception when access my access service:
#Provider
#MyAnnotationToIntercept
public class MyInterceptor implements ContainerRequestFilter {
private MyAccessService accessService;
#Autowired
public MyInterceptor(MyAccessService accessService) {
this.accessService = accessService;
}
public MyInterceptor() {
}
#Override
public void filter(ContainerRequestContext requestContext) {
// DO SOME STUFF Using accessService
}
}
#Component
public class MyAccessService {
private MyDep1 dep1;
#Autowired
public MyAccessService(Mydep1 dep1) {
this.dep1= dep1;
}
}
Is there any way to achieve this? It's really possible?
You will need to use WebApplicationContextUtils's method to get a bean inside filter which is not managed by spring. Here is the example
MyAccessService myAccessService = (MyAccessService) WebApplicationContextUtils.getRequiredWebApplicationContext(httpServletRequest .getServletContext()).getBean(MyAccessService.class);
And to get HttpServletRequest instance you can use #context injection
#Context
private HttpServletRequest httpServletRequest ;
Looks like you have placed #Autowired annotation at the wrong place. It should be above the declaration of accessService. And depending on how you have configured application context, you may/may not need a setter method for accessService instance variable.

Will I have any collisions coming from the #ControllerAdvice if I use it to populate a #ModelAttribute

This is the main controller for the web entrypoint
#Controller
#RequestMapping("/webapp")
public class WebAppController {
#RequestMapping(value = "/home/{authKey}",method = RequestMethod.GET)
String index(#ModelAttribute MyMeta myMeta, Model model){
System.out.println("Token: "+myMeta.getAccessToken());
return "index";
}
#RequestMapping(value = "/config/{authKey}",method = RequestMethod.GET)
String config(#ModelAttribute MyMeta myMeta, Model model){
return "configure";
}
}
Now if you look at the interceptor you can see how I am creating the #ModelAttribute, and see the implementation
#Component
#ControllerAdvice
public class SessionInterceptor implements AsyncHandlerInterceptor {
MyMeta myMeta;
...
#ModelAttribute
public MyMeta getTest() {
return this.myMeta;
}
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o) throws Exception {
...
// parse the key from the request
...
MetaMagicKey metaMagicKey = metaMagicKeyRepo.findKeyByMagicKey(key);
// do work here query my DB and build stuff
...
// assign the queried data built into object
this.myMeta = metaMagicKey.getId().getMyMeta();
return true;
}
My question is, I do not know the true inter-workings of Springboot so I am worried if too many people execute this I might have some object swapping, or some kind of collision? There really isn't a clean way to do this and all of the research I've done is torn between using HttpServletRequest#setAttribute() and using #ModelAttribute, I like the route I chose above as it's VERY easy to implement in my methods.
Springboot 1.4.2 - Java 8
EDIT:
What I ended up trying is this, based on several pages I've read.
I created a new component:
#Component
#RequestScope
public class HWRequest implements Serializable {
private MyMeta myMeta;
public MyMeta getMyMeta() {
return myMeta;
}
public void setMyMeta(MyMeta myMeta) {
this.myMeta = myMeta;
}
}
And then My Config class
#Configuration
public class AppConfig extends WebMvcConfigurerAdapter {
UserSessionInterceptor userSessionInterceptor;
#Autowired
public AppConfig(UserSessionInterceptor userSessionInterceptor) {
this.userSessionInterceptor = userSessionInterceptor;
}
#Bean
#RequestScope
public HWRequest hwRequest() {
return new HWRequest();
}
#Bean
public UserSessionInterceptor createUserSessionInterceptor() {
return userSessionInterceptor;
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(createUserSessionInterceptor()).addPathPatterns("/user/**");
}
}
And here is the interceptor I modified
#Component
#ControllerAdvice
public class SessionInterceptor implements AsyncHandlerInterceptor {
#Resource
HWRequest hwRequest;
...
#ModelAttribute
public HWRequest getTest() {
return this.hwRequest;
}
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o) throws Exception {
...
// parse the key from the request
...
MetaMagicKey metaMagicKey = metaMagicKeyRepo.findKeyByMagicKey(key);
// do work here query my DB and build stuff
...
// assign the queried data built into object
this.hwRequest.setMyMeta(metaMagicKey.getId().getMyMeta());
return true;
}
And of course the modified controller to fit my needs
#Controller
#RequestMapping("/user")
public class WebAppUserController {
#RequestMapping(value = "/home/{authKey}",method = RequestMethod.GET)
String index(#ModelAttribute HWRequest request, Model model){
return "index";
}
#RequestMapping(value = "/config/{authKey}",method = RequestMethod.GET)
String config(#ModelAttribute HWRequest request, Model model){
return "configure";
}
}
Based on all of the documentation I've read this should work, but maybe I am missing something as the interceptor is STILL a singleton. Maybe I am missing something?
myMeta variable represents state in singleton bean. Of course it is not thread-safe and various users will get collisions. Do not ever store any of your application state in singleton beans.
If you want to store some state per request, use Spring's request scope. That means creating separate bean just for storing state annotated with #RequestScope annotation
Reaction on EDIT:
This bean registration can be deleted as it is already registered into Spring IoC container with #Component annotation:
#Bean
#RequestScope
public HWRequest hwRequest() {
return new HWRequest();
}
Another piece that is not needed in your AppConfig is autowiring UserSessionInterceptor bean and registering it as bean again. Delete that. As that bean is being autowired it obviously already is in IoC container, so no need to register it again.
Another confusing piece is workd session in naming. As you are dealing with #RequestScope instead of #SessionScope I would advise to change naming of your class to request (e.g. RequestInterceptor). Session vs Request are very different beasts.
Otherwise it looks like it can work and should be thread safe.

REST service test with JUnit using Java EE annotations

I use Java EE 6 running on GlassFish and Jersey.
How can I mock a request to this resource with some custom headers, and some other request details?
#Stateless
#Path("/user")
public class Resources {
#Contex
private HttpServletRequest request;
....
#GET
#Path("/settings")
#Produces("application/json")
#Consumes("text/plain")
public AccountSettings accountSettings() {
//check custom headers and request content do some stuff
return accountSettings;
}
....
}
And this is my java code, but I get null pointer exception on res.accountSettings() because request is still null.
#Mock
HttpServletRequest request= Mockito.mock(HttpServletRequest.class);
#Test
public void testResources() {
when(request.getHeader(HTTP_AUTHORIZATION_HEADER)).thenReturn("Basic wedwd");
Resources res=new Resources();
AccountSettings response=res.accountSettings();
}
Instead of create with new keyword, When I use this then I also get the resources null.
#Inject Resources resources
I believe you can do as follow with an #InjectMocks annotation, it outside any JAXRS context or CDI injection stuff, it treated as a regular java class.
#RunWith(MockitoJUnitRunner.class)
public class ResourcesTest {
#Mock HttpServletRequest request;
#InjectMocks Resources res;
#Test
public void testResources() {
// given
when(request.getHeader(HTTP_AUTHORIZATION_HEADER)).thenReturn("Basic wedwd");
// when
AccountSettings response = res.accountSettings();
// then
...
}
}

Categories

Resources