I have been chasing down a bug for months where data is incorrectly posted, and
I chased it down to what I believe is a major failing in the interceptor chain.
From what I see through my blurry and tired eyes is that an interceptor list
containing an instance of each interceptor is created for each action mapping.
When an action is called, each interceptor is called in turn. All well in
good... BUT...
These methods are not synchronized. The actionMapping for a given action holds
on to an instance of each interceptor that it calls in turn for every request
(the same instance)! So for example, you have two requests coming in for the
same action at the same time, and it requires both the ServletRequestAware and
SessionAware functionality from ServletConfigInterceptor.
Request 1 enters the interceptor (assigning the context) and is
assigned the request 1 ServletRequestAware information.
Request 2 enters the interceptor (assigning the context) and is
assigned the request 2 ServletRequestAware information.
Request 1 speeds along and is now assigned the request 2
ServletRequestAware information.
Request 2 speeds along and is assigned the request 2
ServletRequestAware information.
Now, I see the keyword final for the context, etc.. in this interceptor, and I
honestly don't know if that makes the methods immune for multiple requests for
synchronized items. Presuming it does, great! However, there are many (and I
mean more than one or two) interceptors which don't make use of the final
keyword, that are not synchronized, and set local variables based on the
action/context that are mutable and can change during another call to the
interceptor.
ParameterRemoverInterceptor...
PrepareInterceptor...
MethodFilterInterceptor...
ScopedModelDrivenInterceptor...
etc. etc...
I searched the documentation, and nowhere does it infer that interceptors were
all prototypes, and from what I see they are functionally prototypes within an
action.
If I am correct here, there are several solutions. However, I am noticing that
Struts is currently failing under heavy loads where there are likely many
requests to a main landing page at the same moment. I have defined my own
interceptors, and without knowing the true architecture, I captured the request
in a class variable then dispatched the request onward to other components for
additional processing... when I get control, I access the request again, and
with the preResultListener method. I noticed that the request magically
changes in the ActionInvocation, and it wasn't until I noticed the object
itself never changing that I first checked spring, and then the struts code for
answers.
So I have been contributing code, and I normally am pretty accurate with these
things. However, I would be very happy to hear that I am incorrect in these
assertions :)
Thanks in advance,
Christian Stone
(References)
StrutsObjectFactory - creates the interceptor.
InterceptorBuilder.constructInterceptorReference() - returns the list.
DefaultConfiguration.buildFullActionConfig() - generates a list of instances
for an action mapping. This list is persistent for the lifecycle of the
application!
and XmlConfigurationProvider (same as DefaultConfiguration).