I'm using Spring 3. When controller gets requests it passes control to method someMethod() annotated with #Async in Service bean and then returns. When I access in someMethod() HttpSession object I receive this exception
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.
How can I resolve this?
The HttpSession object itself can be used in multiple threads (but is not thread-safe and therefore must be synchronized). However Spring is doing some extra magic e.g. when you have session-scoped beans. Namely it uses ThreadLocal underneath to bind current session with thread.
I don't know what is your exact scenario, but apparently Spring tries to retrieve HttpSession from this ThreadLocal while you are in another thread - which obviously fails.
The solution is simple - extract session attributes you need in #Async method and pass them directly. This is by the way much better design - avoid passing HttpSession object around because it makes testing harder and your code is much less likely to be reused in the future.
Related
When ever a spring boot application started, On startup an object is created and we just map the object using #Autowired.
How does only one object serve multiple request? (default configuration is singleton).
If you use the default bean scope which is singleton you need to make them thread safe yourself, in most cases this mean you should keep keep them stateless.
If you need beans that are scoped to a specifik web request you can use the bean scope request for this.
You can read more about this in the documentation.
How does only one object serve multiple request
Normally you have that single object representing a service, a controller, a repository etc...
So you just have a reference for a single instance, and that is enough because in singletons you don't have any state on class level, per request. The http request would normally invoke just some method of that singleton and that is fine, since the method works in it's own scope, per thread execution.
So each request normally uses a different thread to execute and each thread when executing a method of the singleton, uses it's own method parameters in it's own stack frame memory.
Is it possible to disable the reuse of the ServletRequest instance for each request? It looks like the instance is reused multiple times (maybe bound per TCP session?)
What you can see in the spec :
3.13 Lifetime of the Request Object
Each request object is valid only within the scope of a servlet’s service method, or
within the scope of a filter’s doFilter method, unless the asynchronous processing
is enabled for the component and the startAsync method is invoked on the request
object. In the case where asynchronous processing occurs, the request object remains
valid until complete is invoked on the AsyncContext. Containers commonly recycle
request objects in order to avoid the performance overhead of request object
creation. The developer must be aware that maintaining references to request objects
for which startAsync has not been called outside the scope described above is not
recommended as it may have indeterminate results.
In case of upgrade, the above is still true
Containers commonly recycle
request objects in order to avoid the performance overhead of request object
creation
It is container-specific.
In this case it is specific to Jetty.
Jetty recycles the org.eclipse.jetty.server.Request object between requests.
This is normal, is within the definition of the Servlet Spec, and many containers do this.
Be careful of your usage of the HttpServletRequest / ServletRequest / Request object outside of the dispatch from the container.
If you are using it from an AsyncContext you should only use it when you are called by the container, not any other point or from any other thread (unless you are 100% certain you are handling the threading properly, and are ready to handle one of the thousands of corner cases with AsyncContext from a non-container thread)
I'm trying to cache values in a ConcurrentHashMap in the Session. In order to avoid race conditions and ensure that my map is created before any threads attempt to use it, I use HttpSessionListener.sessionCreated() to add the map to the Session:
#Override
public void sessionCreated(HttpSessionEvent event) {
event.getSession()
.setAttribute(MY_CACHE_KEY, new ConcurrentHashMap());
}
Is this code guaranteed to complete before any other threads access the session (via request.getSession() for example)?
I looked at the HttpSessionListener JavaDoc and the Servlet 4.0 Spec. and there don't seem to be any guarantees about thread safety.
The Serlvet Spec. references session thread-safety a few times, but none of these references related to session listeners and session creation as I understand them:
7.7.1 Threading Issues
Multiple servlets executing request threads may have active access to the same session object at the same time. The container must ensure that manipulation of internal data structures representing the session attributes
is performed in a thread safe manner. The Developer has the responsibility for thread safe access to the attribute objects themselves. This will protect the attribute collection inside the HttpSession object from concurrent access,
eliminating the opportunity for an application to cause that collection to become corrupted. Unless explicitly stated elsewhere in the specification (for example Section 7.7.1, “Threading Issues” on page 7-67 for session objects), objects vended from the request or response must be assumed to be non thread safe. This includes, but is not limited to the PrintWriter returned from
ServletResponse.getWriter() and the OutputStream returned from ServletResponse.getOutputStream().
11.5 Listener Instances and Threading
The container is required to complete instantiation of the listener classes in a Web application prior to the start of execution of the first request into the application. The container must maintain a reference to each listener instance until the last request is serviced for the Web application.
Attribute changes to ServletContext and HttpSession objects may occur concurrently. The container is not required to synchronize the resulting notifications to attribute listener classes. Listener classes that maintain state are responsible for the integrity of the data and should handle this case explicitly.
It seems obvious that sessionCreated() must complete before threads have access to the session, but "obviously correct code" has been unsafe for multithreading before.
This ambiguity doesn't exist for ServletContextLister.contextInitialized() since it is guaranteed to complete before Servlet initialization and Servlet.init() is guaranteed to be single-threaded and occur before any requests.
I've tested Tomcat at least and it does ensure that sessionCreated() completes before request.getSession() returns. I tested by putting a breakpoint in sessionCreated() and sending a request which called request.getSession(). This request didn't complete until I continued from the breakpoint. However, one Servlet container implementation's behavior isn't really conclusive proof that all containers/servers behave this way.
Will it be safe to use a bean, with request or session scope, asynchronously in a separate thread that is created from the main thread that handles a request?
I was wondering what will happen if a request completes or a session expires and the child thread is still active. Will spring destroy the bean or is it aware that the bean is still in use.
I'm just familiarizing myself with bean scopes and life cycles in spring. Please pardon my noobness.
Short answer: it is not safe, copy the information you need to do your asynchronous processing.
Long answer: Spring implements request scoped beans using proxies. This proxy is what gets injected into your classes. Whenever you call a method on this proxy, Spring will look up the actual bean that is valid for the current request (using a ThreadLocal mechanism) and then delegate the call to the correct instance.
As soon as the request thread finishes though, the associated request scoped beans are cleared (so as not to interfere with the next time the same thread is used for a different request). They're not "destroyed", but since you only have an indirect reference to them (through the proxy that gets injected) they're effectively unaccessable and will get garbage collected.
If after the request has finished you try to call one of the methods on the proxy and there is no valid request anymore, Spring will throw you an exception.
I am running into a situation where I use ThreadLocal static variable to hold a bean that contains various metrics values from different classes during the lifecycle of the request. In a filter I create the bean and set it in a thread local variable and remove it from the thread local variable in the same filter after request has been processed. What I am running into is that the bean containing values from other requests! The only explanation for this is the thread being shared to process multiple requests at the same time. So the question in the title.
While one thread will generally process a single request (speaking about tomcat, for sure), the thread may process multiple requests over time but not w/o finishing the existing request, unless using include/forward alikes.
I'd VERY strognly recommend you to use attribute (setAttribute()) of the said request w/ your bean and use it for profiling. If you can't provide the request to various methods... well you are stuck w/ the ThreadLocal [which is not so bad solution].
Alternatively you can post the code how you install/remove the threadLocal bean.
Keep in mind that you have to to some managing the of that bean as well (it will not be available outside the request).
Edit: forgot to ask: do you use try/finally calling doFilter(...)?
the code should be like that
installBean();
try{
chain.doFilter(req, resp);
}finally{
Bean b = deinstallBean();
useTheMetrics(b);
//potentially, process exception, etc
}
It could also be that your filter is not always called in the sequence you expect it to be. Threads are reused to process multiple requests one after another, so if the removal of the value in the ThreadLocal does not happen, it will still be there when the thread processes its next request.
Yes, you can assume that a single thread will process each request.
Use a finally block to clear (set to null) the ThreadLocal in the filter after processing the rest of the chain. That will prevent data from previous requests from being mingled with the current request.