Details

Description

I'm trying to get wicket portlet support working over OpenPortal. I've hacked together implementations of ServletContextProvider and PortletResourceURLFactory - just required exposing WicketFilter from the Application to get the necessary data. I can deploy a portlet, but...

Looking at the code, this seems like a clear bug: The method casts to String here:
(String)session.getAttribute(PORTLET_WINDOW_ID);

and if null fills in the value with an instance of Double. If it's going to put a Double there, it should probably not expect a String - this exception will occur any time this method is called twice for a PortletSession, it will throw the CCE the second time it is called.

Since the value is not used, probably simply changing it to
Object portletWindowId = session.getAttribute(PORTLET_WINDOW_ID);

That issue very much relates to this one and if you can read in my comment on PLUTO-516, I suspect the reported errors on PB-84 and PB-77 on other containers like JBoss and OpenPortal
might very well be caused by the exact same implementation mistake.
From that POV, I'm even less inclined now to spend much time trying to find a reliable work around as that most likely just won't be possible if those other containers indeed made the same mistake.
The earlier work around I proposed above (https://issues.apache.org/jira/browse/PB-84?focusedCommentId=12623333#action_12623333) definitely won't work.
As I haven't received the needed feedback for validation on that work around I already concluded it to be invalid, and now I'm even more sure about it.

So, unless someone can come up with another explanation and possible solution for those containers, I'll probably close this issue in the near future as Incomplete/Won't Fix

Ate Douma
added a comment - 22/Oct/08 01:58 FYI: PLUTO-516 has been fixed, see: https://issues.apache.org/jira/browse/PLUTO-516?focusedCommentId=12641680#action_12641680
That issue very much relates to this one and if you can read in my comment on PLUTO-516 , I suspect the reported errors on PB-84 and PB-77 on other containers like JBoss and OpenPortal
might very well be caused by the exact same implementation mistake.
From that POV, I'm even less inclined now to spend much time trying to find a reliable work around as that most likely just won't be possible if those other containers indeed made the same mistake.
The earlier work around I proposed above ( https://issues.apache.org/jira/browse/PB-84?focusedCommentId=12623333#action_12623333 ) definitely won't work.
As I haven't received the needed feedback for validation on that work around I already concluded it to be invalid, and now I'm even more sure about it.
So, unless someone can come up with another explanation and possible solution for those containers, I'll probably close this issue in the near future as Incomplete/Won't Fix

Why does the Pluto PorletSessionImpl#setAttribute use application shop in the following situation? This really needs explanation ala javadoc etc. Because in the spec, it clearly says that setAttribute should use PORTLET_SCOPE as it does in the else block. This seems to be cause the session attributes set in getWindowId in the Apache Bridges to fail in detecting the container assigned window id, because of course the attribute gets put into APPLICATION_SCOPE and a window id is never created.

Ok, yup, changing the setAttribute calls from PortletWindowUtils#getWindowId to call the explicit version of setAttribute(name,value,scope) and pass PortletSession.PORTLET_SCOPE lets the Apache Bridges function work correctly.

This points towards a Pluto issue now, not a bridges issue, so I will create another jira issue for pluto.

Antony Stubbs
added a comment - 20/Oct/08 22:49 Ok, trying to track this down, think I've found something.
Why does the Pluto PorletSessionImpl#setAttribute use application shop in the following situation? This really needs explanation ala javadoc etc. Because in the spec, it clearly says that setAttribute should use PORTLET_SCOPE as it does in the else block. This seems to be cause the session attributes set in getWindowId in the Apache Bridges to fail in detecting the container assigned window id, because of course the attribute gets put into APPLICATION_SCOPE and a window id is never created.
public void setAttribute( String name, Object value, int scope) {
ArgumentUtility.validateNotNull( "attributeName" , name);
if (scope == PortletSession.APPLICATION_SCOPE) {
httpSession.setAttribute(name, value);
} else {
httpSession.setAttribute(createPortletScopedId(name), value);
}
}
Ok, yup, changing the setAttribute calls from PortletWindowUtils#getWindowId to call the explicit version of setAttribute(name,value,scope) and pass PortletSession.PORTLET_SCOPE lets the Apache Bridges function work correctly.
This points towards a Pluto issue now, not a bridges issue, so I will create another jira issue for pluto.
For a work around, I have changed getWindowId to use
session.setAttribute(PORTLET_WINDOW_ID, ...., PortletSession.PORTLET_SCOPE);
in both places where it is used, and the windowId now appears to be generated on the attribute, and detected correctly.
I have created a Pluto issue here:
https://issues.apache.org/jira/browse/PLUTO-516

actually, come to think of it, the StringIndexOutOfBoundsException was probably the reason why the Double wasnt being replaced, as the code threw the exception before it got to that stage, and subsequent requests were retrieving the Double and getting the class cast exceptions.

But regardless, your new implementation does seem to clarify the issues.

Antony Stubbs
added a comment - 17/Oct/08 14:59 actually, come to think of it, the StringIndexOutOfBoundsException was probably the reason why the Double wasnt being replaced, as the code threw the exception before it got to that stage, and subsequent requests were retrieving the Double and getting the class cast exceptions.
But regardless, your new implementation does seem to clarify the issues.

Antony Stubbs
added a comment - 17/Oct/08 14:55 Ok, the new code that you posted seems to do better, but I seem to have a problem where
portletWindowId = nameRef[0].substring( "javax.portlet.p." .length(),
nameRef[0].indexOf('?'));
throws StringIndexOutOfBoundsException, because the value of nameRef [0] comes back just as PORTLET_WINDOW_ID
i.e. "org.apache.portals.bridges.util.portlet_window_id" not "javax.portlet.p.*" as the code implies
getPortletWindowId for session: CC465B39BA9199712DEEF690F90AE2C2 (thread: 15)
no portletWindowId yet - going to derive it.
17/10/2008 15:44:23 org.apache.catalina.core.ApplicationDispatcher invoke
SEVERE: Servlet.service() for servlet default threw exception
java.lang.StringIndexOutOfBoundsException: String index out of range: -17

Right, no CCEs for sure, but what about the PortletWindowId?
Did you check the console output and was the PortletWindowId actually determined, or did you only get a null value returned?
In the latter case, the CCE might be gone but still the functionality is broken and then you still don't have a valid solution...
I'd rather keep the old solution then and have the OpenPortal (and JBoss Portal) team fix the bug in their containers.

Ate Douma
added a comment - 19/Aug/08 08:25 Right, no CCEs for sure, but what about the PortletWindowId?
Did you check the console output and was the PortletWindowId actually determined, or did you only get a null value returned?
In the latter case, the CCE might be gone but still the functionality is broken and then you still don't have a valid solution...
I'd rather keep the old solution then and have the OpenPortal (and JBoss Portal) team fix the bug in their containers.

Thanks for testing it Tom, this was very helpful and confirms my suspicions.
The fact that the internal attribute "org.apache.portals.bridges.util.portlet_window_id" just set as PORTLET_SCOPE session attribute is not returned from the APPLICATION_SCOPE enumeration of attributes clearly is not compliant with JSR-168 PLT.15.3.

I have no idea how OpenPortal and JBoss Portal (see also: PB-77) are implementing their session attributes handling but IMHO this should be filed as a bug against both containers.

Nonetheless, I've been thinking of an alternative solution which hopefully might work on these containers too.

I've rewritten the getPortletWindowId() method as follows which works just as well on Pluto/Jetspeed-2 (which might even be a more optimal/efficient solution).
If this works for OpenPortal I'm willing to replace the current solution with this new one:

Again, I've attached a portal-bridges-commons-1.0.4.jar (replacing the previous one) with this method compiled in. (FYI: this one is build for Java 1.4 again).
I get the following (expected) output with Pluto/Jetspeed-2:

Can you please again try this one out on OpenPortal and report back if this works.

Also note: as you can see I'm now removing the PORTLET_WINDOW_ID attribute again regardless if a the SessionBindingListener solution works,
so you should no longer encounter a CCE with this version, but won't get the requested PortletWindowId either (which then probably will break other depending code...)

Ate Douma
added a comment - 18/Aug/08 13:24 Thanks for testing it Tom, this was very helpful and confirms my suspicions.
The fact that the internal attribute "org.apache.portals.bridges.util.portlet_window_id" just set as PORTLET_SCOPE session attribute is not returned from the APPLICATION_SCOPE enumeration of attributes clearly is not compliant with JSR-168 PLT.15.3.
I have no idea how OpenPortal and JBoss Portal (see also: PB-77 ) are implementing their session attributes handling but IMHO this should be filed as a bug against both containers.
Nonetheless, I've been thinking of an alternative solution which hopefully might work on these containers too.
I've rewritten the getPortletWindowId() method as follows which works just as well on Pluto/Jetspeed-2 (which might even be a more optimal/efficient solution).
If this works for OpenPortal I'm willing to replace the current solution with this new one:
public static String getPortletWindowId(PortletSession session)
{
System.out.println("getPortletWindowId for session: " session.getId() " (thread: " Thread.currentThread().getId() ")");
String portletWindowId = (String)session.getAttribute(PORTLET_WINDOW_ID);
if ( portletWindowId == null )
{
synchronized (session)
{
System.out.println(" no portletWindowId yet - going to derive it.");
final String[] nameRef = new String [1] ;
session.setAttribute(PORTLET_WINDOW_ID, new HttpSessionBindingListener()
{
public void valueBound(HttpSessionBindingEvent event)
{
nameRef[0] = event.getName();
}
public void valueUnbound(HttpSessionBindingEvent event) {}
});
session.removeAttribute(PORTLET_WINDOW_ID);
if (nameRef [0] != null)
{
portletWindowId = nameRef[0].substring("javax.portlet.p.".length(),nameRef[0].indexOf('?'));
System.out.println(" --> found - PORTLET_WINDOW_ID = "+portletWindowId);
session.setAttribute(PORTLET_WINDOW_ID, portletWindowId);
}
else
{
System.out.println(" --> error: failed to derive the portletWindowId");
}
}
}
return portletWindowId;
}
Again, I've attached a portal-bridges-commons-1.0.4.jar (replacing the previous one) with this method compiled in. (FYI: this one is build for Java 1.4 again).
I get the following (expected) output with Pluto/Jetspeed-2:
getPortletWindowId for session: F8DB1E64E9C8227B0CA4A10BDB6E231A (thread: 53)
no portletWindowId yet - going to derive it.
--> found - PORTLET_WINDOW_ID = jp-2
Can you please again try this one out on OpenPortal and report back if this works.
Also note: as you can see I'm now removing the PORTLET_WINDOW_ID attribute again regardless if a the SessionBindingListener solution works,
so you should no longer encounter a CCE with this version, but won't get the requested PortletWindowId either (which then probably will break other depending code...)

> If you look at the rest of following code within this method, you'll notice the random
> generated Double object put in the session is put in there only for a for a very short time
> and replaced within the same method almost immediately with a String object.

Okay, now I understand what you're trying to do here - that helps. I now understand what the use of random is about, and
portletWindowId = value;
is definitely wrong.

But portletWindowId is only reset if two tests are true -
names.hasMoreElements()
and
PortletSessionUtil.decodeAttributeName(name).equals(PORTLET_WINDOW_ID) && value.equals(session.getAttribute(name,PortletSession.APPLICATION_SCOPE)

otherwise the Double remains there, and a subsequent call to the same method will trigger a CCE. From your description, it does not sound like you intend to leave the random Double there, but if either of the two tests above fails, that is what it will do. If either of the two tests failing means that the environment is completely unusable (I don't know portlets well enough to be sure if that's true or not), some kind of failure may be inevitable, but it would be better to restore the value and not have the failure look like it is in your code.

I don't work on portal software at Sun(well, I spent six hours today inside OpenPortal's code, but that wasn't for Sun), so I don't know the history of why this is the way it is with JBoss or OpenPortal - I'm just trying to help Jon get some work done.

I agree if the spec says it should be a string, then it should be a string; I could also believe that if the server is really putting it as some other type, that both companies might have customers who are depending on it being a Double (unless this is really just a result of this method being called twice and one of the tests failing), and be at risk if they break backward compatibility to comply with the spec.

But my first assumption would be that this code is being called twice, the second time after putting a Double into the map and failing one of the tests in getPortletWindowId().

Tim Boudreau
added a comment - 18/Aug/08 06:38 > If you look at the rest of following code within this method, you'll notice the random
> generated Double object put in the session is put in there only for a for a very short time
> and replaced within the same method almost immediately with a String object.
Okay, now I understand what you're trying to do here - that helps. I now understand what the use of random is about, and
portletWindowId = value;
is definitely wrong.
But portletWindowId is only reset if two tests are true -
names.hasMoreElements()
and
PortletSessionUtil.decodeAttributeName(name).equals(PORTLET_WINDOW_ID) && value.equals(session.getAttribute(name,PortletSession.APPLICATION_SCOPE)
otherwise the Double remains there, and a subsequent call to the same method will trigger a CCE. From your description, it does not sound like you intend to leave the random Double there, but if either of the two tests above fails, that is what it will do. If either of the two tests failing means that the environment is completely unusable (I don't know portlets well enough to be sure if that's true or not), some kind of failure may be inevitable, but it would be better to restore the value and not have the failure look like it is in your code.
I don't work on portal software at Sun(well, I spent six hours today inside OpenPortal's code, but that wasn't for Sun), so I don't know the history of why this is the way it is with JBoss or OpenPortal - I'm just trying to help Jon get some work done.
I agree if the spec says it should be a string, then it should be a string; I could also believe that if the server is really putting it as some other type, that both companies might have customers who are depending on it being a Double (unless this is really just a result of this method being called twice and one of the tests failing), and be at risk if they break backward compatibility to comply with the spec.
But my first assumption would be that this code is being called twice, the second time after putting a Double into the map and failing one of the tests in getPortletWindowId().
I'll try your patch and post output here.

The above is an incorrect assessment of how the code works.
And, although this "issue" has been brought up before, in my view the CCE thrown by OpenPortal (as well as JBoss Portal AFAIK) is a bug in the portlet container, not this method.

If you look at the rest of following code within this method, you'll notice the random generated Double object put in the session is put in there only for a for a very short time
and replaced within the same method almost immediately with a String object.

The whole point of this method is determining what the container provided PORTLET_WINDOW_ID is using a JSR-168 compliant "trick", in that PORTLET_SCOPE session attributes
are stored within the session using a predefined format which includes the PORTLET_WINDOW_ID (see: JSR-168 spec PLT.15.3).

For that purpose, a reasonably unique (double) value is generated using Math.random() and stored as a Double value within the session under PORTLET_SCOPE for an internal defined attribute name, "org.apache.portals.bridges.util.portlet_window_id".
Thereafter, all the APPLICATION_SCOPE attributes are evaluated, looking again for this internal attribute name ("org.apache.portals.bridges.util.portlet_window_id") and (as an extra save guard)
its value is checked against the previously generated Double value to be sure the correct attribute value (name) is found.

Then, the temporary Double value is replaced with the real (String type) PORTLET_WINDOW_ID.

If the portlet container is implemented and behaving compliant to JSR-168, the above routine should work as expected and the temporary Double value should always be replaced again with a String value within the same method.
Also note: this part of the method is executed within a sychronized block so interference of other requests should not be possible either.

The fact this method fails (sometimes?) on OpenPortal and JBoss Portal in my view is very suspicious.
Somehow, the "org.apache.portals.bridges.util.portlet_window_id" attribute stored under PORTLET_SCOPE seems not be found correctly under APPLICATION_SCOPE, in which case this method will return null (this first time).
Subsequently, the following call to this method will indeed cause a CCE, sure.

Although I'd like to "fix" this problem, or even use a proper work around if possible, simply using the (obviously a Double) value as an Object won't help here.
Note: this method is supposed to return a String value and specifically a PORTLET_WINDOW_ID.

Until it is more clear what goes wrong here and why these portlet containers are not behaving as expected, I have no real solution available.

Tim, as you're encountering this problem with OpenPortal, it would be great if you could try to determine in more detail what goes on here.
I've created a modified version of the getPortletWindowId method which will produce some stack trace logging to the console which might tell us more (note: requires Java 1.5):

Ate Douma
added a comment - 17/Aug/08 22:09 - edited The above is an incorrect assessment of how the code works.
And, although this "issue" has been brought up before, in my view the CCE thrown by OpenPortal (as well as JBoss Portal AFAIK) is a bug in the portlet container, not this method.
If you look at the rest of following code within this method, you'll notice the random generated Double object put in the session is put in there only for a for a very short time
and replaced within the same method almost immediately with a String object.
The whole point of this method is determining what the container provided PORTLET_WINDOW_ID is using a JSR-168 compliant "trick", in that PORTLET_SCOPE session attributes
are stored within the session using a predefined format which includes the PORTLET_WINDOW_ID (see: JSR-168 spec PLT.15.3).
For that purpose, a reasonably unique (double) value is generated using Math.random() and stored as a Double value within the session under PORTLET_SCOPE for an internal defined attribute name, "org.apache.portals.bridges.util.portlet_window_id".
Thereafter, all the APPLICATION_SCOPE attributes are evaluated, looking again for this internal attribute name ("org.apache.portals.bridges.util.portlet_window_id") and (as an extra save guard)
its value is checked against the previously generated Double value to be sure the correct attribute value (name) is found.
Then, the temporary Double value is replaced with the real (String type) PORTLET_WINDOW_ID.
If the portlet container is implemented and behaving compliant to JSR-168, the above routine should work as expected and the temporary Double value should always be replaced again with a String value within the same method.
Also note: this part of the method is executed within a sychronized block so interference of other requests should not be possible either.
The fact this method fails (sometimes?) on OpenPortal and JBoss Portal in my view is very suspicious.
Somehow, the "org.apache.portals.bridges.util.portlet_window_id" attribute stored under PORTLET_SCOPE seems not be found correctly under APPLICATION_SCOPE, in which case this method will return null (this first time).
Subsequently, the following call to this method will indeed cause a CCE, sure.
Although I'd like to "fix" this problem, or even use a proper work around if possible, simply using the (obviously a Double) value as an Object won't help here.
Note: this method is supposed to return a String value and specifically a PORTLET_WINDOW_ID.
Until it is more clear what goes wrong here and why these portlet containers are not behaving as expected, I have no real solution available.
Tim, as you're encountering this problem with OpenPortal, it would be great if you could try to determine in more detail what goes on here.
I've created a modified version of the getPortletWindowId method which will produce some stack trace logging to the console which might tell us more (note: requires Java 1.5):
public static String getPortletWindowId(PortletSession session)
{
System.out.println("getPortletWindowId for session: " session.getId() "(thread: " Thread.currentThread().getId() ")");
String portletWindowId = (String)session.getAttribute(PORTLET_WINDOW_ID);
if ( portletWindowId == null )
{
synchronized (session)
{
System.out.println(" no portletWindowId yet - going to derive it. Current thread (" Thread.currentThread().getId() ") call stack:");
for (StackTraceElement st : Thread.currentThread().getStackTrace()) System.out.println(st);
Double value = new Double(Math.random());
session.setAttribute(PORTLET_WINDOW_ID, value);
Enumeration names = session.getAttributeNames(PortletSession.APPLICATION_SCOPE);
System.out.println(" looking for attribute " PORTLET_WINDOW_ID " in current APPLICATION_SCOPE attributes:");
while (names.hasMoreElements())
{
String name = (String)names.nextElement();
System.out.println(" found attribute: "+name);
if (PortletSessionUtil.decodeAttributeName(name).equals(PORTLET_WINDOW_ID) && value.equals(session.getAttribute(name,PortletSession.APPLICATION_SCOPE)) )
{
portletWindowId = name.substring("javax.portlet.p.".length(),name.indexOf('?'));
System.out.println(" --> found - PORTLET_WINDOW_ID = "+portletWindowId);
session.setAttribute(PORTLET_WINDOW_ID, portletWindowId);
break;
}
}
if (portletWindowId == null) System.out.println(" --> error: failed to derive the portletWindowId");
}
}
return portletWindowId;
}
For your convenience I've attached a modified version of the portlet-bridges-common-1.0.4.jar with the above changed method compiled in it (again: using it requires Java 5).
I've run it myself (on Pluto/Jetspeed-2) which produces the following output (stack trace dump truncated for brevity):
getPortletWindowId for session: FC9065541BED56975E35C1AD97BAB419(thread: 55)
no portletWindowId yet - going to derive it. Current thread (55) call stack:
java.lang.Thread.dumpThreads(Native Method)
java.lang.Thread.getStackTrace(Thread.java:1383)
org.apache.portals.bridges.util.PortletWindowUtils.getPortletWindowId(PortletWindowUtils.java:53)
org.apache.portals.bridges.util.ServletPortletSessionProxy.createProxy(ServletPortletSessionProxy.java:59)
org.apache.portals.bridges.struts.StrutsPortlet.processRequest(StrutsPortlet.java:420)
org.apache.portals.bridges.struts.StrutsPortlet.doView(StrutsPortlet.java:301)
javax.portlet.GenericPortlet.doDispatch(GenericPortlet.java:247)
javax.portlet.GenericPortlet.render(GenericPortlet.java:175)
org.apache.jetspeed.factory.JetspeedPortletInstance.render(JetspeedPortletInstance.java:103)
org.apache.jetspeed.container.JetspeedContainerServlet.doGet(JetspeedContainerServlet.java:277)
javax.servlet.http.HttpServlet.service(HttpServlet.java:690)
<snip/>
looking for attribute org.apache.portals.bridges.util.portlet_window_id in current APPLICATION_SCOPE attributes:
found attribute: javax.portlet.p.jp-2?org.apache.portals.bridges.util.portlet_window_id
--> found - PORTLET_WINDOW_ID = jp-2
If you could try the same and provide me with the debug output you get, maybe we can get somewhat further with this problem.
Ate