Error when calling session object inside Method decorated by Spring websocket annotations - session

Using the Grails spring-websocket plugin:
CurrentStatusController.groovy
#MessageMapping("/personExist")
#SendTo("/topic/personExist")
protected Boolean personExist(String personId) {
return (Person.get(personId)!=null)
}
Saving all id of persons in session list personIds, then handle the same method using a session object
CurrentStatusController.groovy
#MessageMapping("/personExist")
#SendTo("/topic/personExist")
protected Boolean personExist(String personId) {
return (session.personIds.contains(personId))
}
The first works, the last does not work with the following error message :
ERROR springwebsocket.GrailsSimpAnnotationMethodMessageHandler -
Unhandled exception Message: No thread-bound request found: Are you
referring to request attributes outside of an actual web request, or
processing a request outside of the originally receiving thread? If
you are actually operating within a web request and still receive this
message, your code is probably running outside of
DispatcherServlet/DispatcherPortlet: In this case, use
RequestContextListener or RequestContextFilter to expose the current
request.
Line | Method
->> 53 | getSession in org.grails.plugins.web.rest.api.ControllersRestApi
How to make Spring-WebSocket methods accepts session object?

Related

Request Scope beans in servlet 3 async Controler

I'm using a Servlet 3 controller in a Spring Boot application. This controller calls services that, in the end, make 3 HTTP requests.
#GetMapping("/nbasync/c/**")
public CompletableFuture<String> nonBlockingAsyncCall() throws Exception {
CompletableFuture<String> result = service.call());
CompletableFuture<Void> result2 = service2.call();
CompletableFuture<Void> result3 = service3.call();
return result
.thenCombine(result2, this::keepFirst)
.thenCombine(result3, this::keepFirst);
}
Each of this outgoing calls are made using a RestTemplate and are intercepted by a ClientHttpRequestInterceptor. In this ClientHttpRequestInterceptor, I need a (proxied) request scoped bean (cf: How to enable request scope in async task executor with a Runnable).
This works just fine if I wait for the the result :
CompletableFuture.allOf(result, result2, result3).join();
return result.get();
In the non blocking method, it crashes with the following exception :
java.util.concurrent.CompletionException:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'scopedTarget.requestCookieHelper': Scope 'request' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is
java.lang.IllegalStateException: Cannot ask for request attribute - request is not active anymore!
See complete log : https://gist.github.com/Skeebl/d0b19ebb9ab4d0d2a917203e4bd6fad5
It appears that each time a thread lets go of the process, AbstractRequestAttributes.requestCompleted() is called (twice). This methods sets this.requestActive = false;. While requestActive is false, you can't access the request scoped beans.
The interceptor and request scoped bean simplify the methods signatures. Is there a way to keep theses while working with async requests ?

How to handle HttpMediaTypeNotAcceptableException by writing error content to the response body using exception handler annotation?

When a client request for a resource producing application/json content with Accept Header of application/xml. The request fails with HttpMediaTypeNotAcceptableException exception and is wrapped into error message body in the response entity object by using exception handler annotation as mentioned in below code. However, we receive HttpMediaTypeNotAcceptableException again when return values are written to the response with HttpMessageConverter. It is because it checks the producible content type for the response with the acceptable request type, but this is exactly something we are trying to communicate to the client using error message. How do I workaround this issue ? Btw, all the other exceptions are parsing fine to error message. Please advise.
#ControllerAdvice
public class RestExceptionHandler extends ResponseEntityExceptionHandler {
#Override
protected ResponseEntity<Object> handleExceptionInternal(Exception ex, Object body,
HttpHeaders headers, HttpStatus status, WebRequest request) {
// Setting the response content type to json
headers.setContentType(MediaType.APPLICATION_JSON);
return ResponseEntity.status(status).headers(headers).body(body);
}
}
A few options come to my mind. One is that your controller method produces all content types and then you throw an exception in your method if the content type is not the one you are expecting, then the exception handler can take this exception and transform it. This is the only one that works with exception handlers, as exception handlers only deal with exceptions produced in the controller method.
The other options are:
Use an interceptor (but I'm not sure if this will work, as Spring might try to resolve first the controller method rather than invoking the interceptors).
Extend RequestMappingHandlerMapping to call the exception handler if it doesn't find a suitable method. You'll probably need to override the method handleNoMatch. In there you'll need to get a reference to the list of HandlerExceptionResolver
The first one is the simplest to understand, and the latest one might be the most 'extensible', but it also requires some understanding of the internals of Spring.
Resolved by setting different content negotiation strategy FixedContentNegotiationStrategy for ExceptionHandlerExceptionResolver and HeaderContentNegotiationStrategy for RequestResponseBodyMethodProcessor.
I have been using a serialized enum-based response (enum annotated with jackson #JsonFormat(shape = Shape.OBJECT) to standardize the error messages in my exception handler class and faced the same issue when it caught with a HttpMediaTypeNotAcceptableException.
The workaround is to set the media type you expect to return directly to the builder method available in the ResponseEntity.
The below code works fine for me.
#ExceptionHandler(HttpMediaTypeNotAcceptableException.class)
public final ResponseEntity<ResponseMessagesEnum> handleHttpMediaTypeNotAcceptableException(
HttpMediaTypeNotAcceptableException e, HttpServletRequest request) {
logger.error("No acceptable representation found for [{}] | supported {}", request.getHeader("Accept"), e.getSupportedMediaTypes());
return ResponseEntity.status(HttpStatus.BAD_REQUEST).contentType(MediaType.APPLICATION_JSON)
.body(ResponseMessagesEnum.EX_001);
}

Grails: User logs out while ajax request is running

There is a Grails (v.2.3.2) web app with Spring Security Core plugin (v.2.0-RC2).
I stumbled upon an issue with users who log out while there is an ajax request running in the background.
The scenario is as follows:
User requests a web page
When the page is ready I fire an ajax request
User logs out while the ajax request is still being processed on the server side
The server side, naturally, heavily depends on the current user, and the app crushes on the third step because the current user suddenly disappears as the springSecurityService indicates that the user is not logged in.
This is the code I used to fetch the current user in the UserService.
public User getLoggedInUser() {
if (!springSecurityService.isLoggedIn()) {
return null
}
User user = User.get(springSecurityService.getPrincipal().id)
user
}
Which, returns the current user alright up until the moment the user logs out, causing the issue.
I came up with the idea to make the UserService stateful and store the current user in a separate field.
static scope = 'request' // create a new instance for every request
private Long currentUserId = null
public User getLoggedInUser() {
if (!currentUserId) {
if (!springSecurityService.isLoggedIn()) {
return null
}
// Store the ID of the current user in the instance variable.
currentUserId = springSecurityService.getPrincipal().id
}
// Fetch and return the user.
return User.get(currentUserId)
}
In addition, I created a new Spring bean which defines a proxy object for my UserService.
userServiceProxy(ScopedProxyFactoryBean) {
targetBeanName = 'userService'
proxyTargetClass = true
}
Now, this works very well for the most scenarios, but fails when there is no web request present. In particular, in BootStrap.groovy, where I use other services of my application.
This is the error message I get:
Error initializing the application: Error creating bean with name 'scopedTarget.userServiceProxy': Scope 'request' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
Any suggestions on how to fix this?
After some investigation and lots of swear words the solution was finally found.
This is the code I use in BootStrap.groovy to mimic an ongoing web request.
class BootStrap {
def init = { ServletContext servletContext ->
// Mock request and response.
HttpServletRequest request = new MockHttpServletRequest(servletContext)
HttpServletResponse response = new MockHttpServletResponse()
// Now store them in the current thread.
GrailsWebRequest grailsRequest = new GrailsWebRequest(request, response, servletContext)
WebUtils.storeGrailsWebRequest(grailsRequest)
/**
* Perform whatever you need to do that requires an active web request.
*/
}
}

How to handle session expired exception in Spring MVC-Spring Security app for GWT RPC calls

I have Spring MVC application where security is handled by Spring Security.
UI is built using GWT which gets the data from server using RPC approach.
I need to handle on UI the situation when session is expired:
For example RPC AsyncCallback can get SessionExpiredException type of exception and popup the window with message like "You session is expired, please click the refresh link" or something.
Did someone deal with such problem?
Thanks.
I suppose that for processing of incoming GWT call you use some Spring MVC controller or some servlet. It can have following logic
try{
// decode payload from GWT call
com.google.gwt.user.server.rpc.RPC.decodeRequest(...)
// get spring bean responsible for actual business logic
Object bean = applicationContext.getBean(beanName);
// execute business logic and encode response
return RPC.invokeAndEncodeResponse(bean, ….)
} catch (com.google.gwt.user.server.rpc.UnexpectedException ex) {
// send unexpected exception to client
return RPC.encodeResponseForFailure(..., new MyCustomUnexpectedException(), …) ;
}
Solution for this case
HttpServletRequest request = getRequest() ;
if (request.getRequestedSessionId() != null && !request.isRequestedSessionIdValid()) {
return RPC.encodeResponseForFailure(..., new MyCustomSessionExpiredException(), …) ;
} else {
// first code snippet goes here
}
Then catch custom session expired exception in a client side code. If you do not use RPC directly then provide more details about your bridge implementation between GWT and Spring.
You will need also force GWT compiler to include MyCustomSessionExpiredException type to a serialization white list (to prevent case when GWT security policy stops propogation of the exception to client side). Solution: include MyCustomSessionExpiredException type to each method signature of each synchronous interface:
#RemoteServiceRelativePath("productRpcService.rpc")
public interface ProductRpcService extends RemoteService {
List<Product> getAllProducts() throws ApplicationException;
void removeProduct(Product product) throws ApplicationException;
}
MyCustomSessionExpiredException extends ApplicationException
Then show pop-up in client side code:
public class ApplicationUncaughtExceptionHandler implements GWT.UncaughtExceptionHandler {
#Override
public void onUncaughtException(Throwable caught) {
if (caught instanceof MyCustomSessionExpiredException) {
Window.alert("Session expired");
}
}
}
// Inside of EntryPoint.onModuleLoad method
GWT.setUncaughtExceptionHandler(new ApplicationUncaughtExceptionHandler());
I researched a bit and uploaded the solution here http://code.google.com/p/gspring/source/browse/#svn%2Ftrunk%2Fsample%2Fsession-expired%253Fstate%253Dclosed.
Use mvn jetty:run-war to see the demo after checking it out and go to rpc-security-sample/index.htm
There are two ways to solve it.
The first is around to pass the delegate proxy for GWT RemoteServlet which throws SessionExpiredException during method invocation. This requires to declare Exception in every RPC service method. Example: http://code.google.com/p/gspring/source/browse/#svn%2Ftrunk%2Fsample%2Fsession-expired%253Fstate%253Dclosed
Steps:
Develop new filter which intercepts first
Declare SessionExpiredException in each RPC method service which could inherit RuntimeException for simplicity (no need to follow this in implementers)
Develop parent generic AsyncCallback handler
Use http://code.google.com/p/gspring/ solution to handle all incoming RCP requests.
The second which is much more simplest: return the 401 HTTP error and handle in UI side (GWT native general exception contains the HTTP status number). Example: http://code.google.com/p/gspring/source/browse/#svn%2Ftrunk%2Fsample%2Fsession-expired-401
The second approach is simplest and does not require declaring Exception in service methods contract. However following the first approach can give you some flexibility: it could contain some additional info like last login time (for SessionExpiredException) etc. Also the second approach can introduce new exceptions which are inherited from SecurityException like blacklisted user (for example if user was blacklisted during his session) or for example if user does the same actions very often like a robot (it could be asked for passing the captcha) etc.

Getting Request object from HttpSessionEvent

I have a Session listener which extends PortalSessionListener. I have sessionCreated(HttpSessionEvent httpSessionEvent) and sessionDestroyed(HttpSessionEvent httpSessionEvent) methods
When my Session gets invalidated (after 15 mins as per my configuration in web.xml), my listener is called and Session is invalidated.
In my listener I want to clear off Cookie values before logging out the User. So, I want Request and Response objects so that I can clear off Cookie values and set it in Response.
But, how can I get Request / Response objects in my listener which has HttpSessionEvent?
I tried below code. But, this is not getting invoked when my sessionDestroyed method is called or any other phase for that matter.
public void requestInitialized(ServletRequestEvent servletRequestEvent)
{
log.debug("Entered into requestInitialized method");
HttpServletRequest request = (HttpServletRequest) servletRequestEvent.getServletRequest();
log.debug("Request object created is :" +request);
}
It has been suggested that implementing a Filter suits this requirement (for getting Request object). How that can be applied to my scenario?
This might be realated:
you can access the RequestContextHolder and get the value
String ipAddr =
((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes())
.getRequest().getRemoteAddr();
Posted here

Resources