History | Log In     View a printable version of the current page.  
Issue Details (XML | Word | Printable)

Key: UP-1123
Type: Bug Bug
Status: Closed Closed
Resolution: Fixed
Priority: Major Major
Assignee: Faizan Ahmed
Reporter: Faizan Ahmed
Votes: 0
Watchers: 0
Operations

If you were logged in you would be able to see more operations.
uPortal

PortletStateManager.clearState(PortletWindow) implementation is buggy and can lead to ConcurrentModificationException under stress.

Created: 27/May/05 11:01 AM   Updated: 03/Jul/07 04:11 PM
Component/s: Portlet Container
Affects Version/s: 2.4, 2.4.1, 2.4.2, 2.5.0 M1, 2.5.0 RC1, 2.5.0 RC3, 2.5.0 RC2, 2.5.0 GA
Fix Version/s: 2.4.3

Original Estimate: Unknown Remaining Estimate: Unknown Time Spent: Unknown
Issue Links:
Duplicate
 
This issue is duplicated by:
UP-1124 PortletStateManager.clearState(Portle... Major Closed


 Description  « Hide
PortletStateManager.clearState(PortletWindow) implementation is buggy and can lead to ConcurrentModificationException under stress.

Under stress, you can
run into the following scenaro which produces this stack trace.

java.util.ConcurrentModificationException
      at java.util.HashMap$HashIterator.nextEntry(HashMap.java:782)
      at java.util.HashMap$KeyIterator.next(HashMap.java:818)
      at org.jasig.portal.container.services.information.PortletStateManager.clearState(PortletStateManager.java:355)
      at
org.jasig.portal.channels.portlet.CPortletAdapter.receiveEvent(CPortletAdapt
er.java:345)
      at
org.jasig.portal.MultithreadedCharacterChannelAdapter.receiveEvent(Multithre
adedCharacterChannelAdapter.java:62)
      at
org.jasig.portal.ChannelManager.finishedSession(ChannelManager.java:269)

After examining the code, it looks like you'd get this exception when two
users end/begin the lifecycle of this channel at the same time (likely under
stress??), since they both have access to the same Map object at the same
time -- classic concurrency stuff. And once the remove operation throws
that exception, I believe the ChannelState object get left behind. It looks
like remove's and put's into the channelStateMap need to broken out into
synchronized methods.

 All   Comments   Work Log   Change History      Sort Order:
Faizan Ahmed [27/May/05 11:31 AM]
Following are some of the email exchanges happened between Nick, Scott and Faizan. I am posting these for clarifications.

I'm concerned with the implementation of the clearState method and
actually the PortletStateManager in general.

The fact that we cannot use normal get or remove methods to
retrieve/remove items from the map when we have the key implies there
may be something wrong with the implementation.

It seems like we should have a Map keyed on geyKey(portletWindow) and
then the Map stores Maps which are keyed on whatever additional
information was used in the first Key.

Then we can do something like this:
Map map = <whereever we got map from>
String key = getKey(portletWindow) <-- can we even just key on portlet
windows???)
Map portletWindowMap = map.get(key);
portletWindowMap.clear();
map.remove(key);

However this would most likely require other code to be rewritten (and
is incompatible with clearState(HttpServletRequest). What are the use
cases when clearState(HttpServletRequest) would be called vs.
clearState(PortletWindow)

Also, getKey is checking if a session exists (clearState does this
also) but it calls request.getSession() which according to the JavaDoc
does this: "Returns the current session associated with this request, or
if the request does not have a session, creates one."


Faizan Ahmed [27/May/05 11:32 AM]
> But you make a good point. It does look like they are trying to store a
> flat map of current and previous states .. instead of storing a generic
> Map it might be better to store an Object with currentState and prevState
> attributes.
>
>
>
The way its implemented now, you don't even need a Map. A Set would be
sufficient since they are just iterating through a collection of keys to
find a match (you could just iterate through a set and find any
matches). As I don't know the code base too well I won't attempt to
speculate on the proper way to store it in a map, but I can say that in
the current implementation, the fact that the stuff is stored in a map
appears not have any useful function other than quick gets later on.

>
>
>> Also, getKey is checking if a session exists (clearState does this
>> also) but it calls request.getSession() which according to the JavaDoc
>> does this: "Returns the current session associated with this request, or
>> if the request does not have a session, creates one."
>>
>>
>
> This was part of the problem that led to the first memory leak (well second
> actually) in CPortletAdapter. Since the key had previously depended on
> having a valid session, it threw an exception trying to obtain it when the
> HttpServletResponse object had already been committed. To get around this
> we provided a fix that modified getKey to use the window's ID. We had also
> changed CPortletAdapter to set the PortletWindow's ID to include the session
> Id. This does imply that we require PortletWindow IDs to be instance
> unique. I couldn't find anywhere in the spec which indicates a violation.
>
>
My point was that the method was doing additional checks.
HttpServletRequest.getSession() always returns a session, regardless
of whether one originally existed or not (it will create a new one).
Thus an if (session == null) check is redundant.
HttpServletRequest.getSession(false) would need the check.

Just an additional question.. Is there a reason the current state is
not stored with the PortletWindow so you could say something like
portletWindow.getCurrentState() as opposed to checking a map? It seems
like each portletWindow is unique and holds some kind of state (since it
has its own HttpServletRequest). If this was possible, it would
eliminate any need for a map to hold this information.


Faizan Ahmed [27/May/05 11:34 AM]
The current code in portal_rel-2-4-patches is quite different than the portal_rel-2-5-patches and in the head that is why I created this issue separatly.