Pluto
  1. Pluto
  2. PLUTO-590

Nested dispatching from a portlet to two or more servlet/jsps does not retain the portlet contextPath when calling (servlet)request.getContextPath() but returns the portal contextPath

    Details

    • Type: Bug Bug
    • Status: Resolved
    • Priority: Critical Critical
    • Resolution: Fixed
    • Affects Version/s: 2.0.0, 2.0.1
    • Fix Version/s: 2.0.2, 2.1.0
    • Component/s: portlet container
    • Labels:
      None

      Description

      JSPs called by a portlet have this problem. request.getContextPath() changed between pluto 1.x and 2.0

      You need to use the portlet taglib to get the context path from the portletrequest.

        Activity

        Hide
        Ate Douma added a comment -

        Thanks for validating Eric, resolving now as fixed.

        Show
        Ate Douma added a comment - Thanks for validating Eric, resolving now as fixed.
        Hide
        Eric Dalquist added a comment -

        The fix works for me.

        I agree that the really exotic use-cases are going to be too much to try and support, I think where we are now is just fine.

        Show
        Eric Dalquist added a comment - The fix works for me. I agree that the really exotic use-cases are going to be too much to try and support, I think where we are now is just fine.
        Hide
        Ate Douma added a comment -

        Fix committed.

        Fixing this issue turned out not too difficult, at least for the most logical/common use-cases.
        Very exotic use-cases like dispatching from a Portlet to a servlet which in turn (tries to) use cross-context dispatching, using either/both including and/or forwarding, really would be too complicated to always handle correctly.
        Cross-context dispatching actually is very much underspecified in the servlet spec. itself, let alone the portlet spec. which doesn't say anything about it at all.
        As the servlet spec and portlet spec already are not fully aligned in this area, I think we can only do as much as reasonably possible.
        The use-case of this issue however clearly falls within the scope of what we can and should handle and the fix I applied solves it appropriately imo.

        Show
        Ate Douma added a comment - Fix committed. Fixing this issue turned out not too difficult, at least for the most logical/common use-cases. Very exotic use-cases like dispatching from a Portlet to a servlet which in turn (tries to) use cross-context dispatching, using either/both including and/or forwarding, really would be too complicated to always handle correctly. Cross-context dispatching actually is very much underspecified in the servlet spec. itself, let alone the portlet spec. which doesn't say anything about it at all. As the servlet spec and portlet spec already are not fully aligned in this area, I think we can only do as much as reasonably possible. The use-case of this issue however clearly falls within the scope of what we can and should handle and the fix I applied solves it appropriately imo.
        Hide
        Ate Douma added a comment -

        Eric,

        I've now been able to reproduce the error: it is a bug in HttpServletPortletRequestWrapper indeed, method setupNestedDispatchPathValues() to be precise.
        This bug only shows up when going through multiple/nested dispatches like in your BookmarksPortlet but I also could easily reproduce it with using just two minimal servlets chaining a request dispatcher include call.
        Too bad the Portlet 2.0 TCK doesn't include tests for nested dispatching (which semantics is largely underspecified in the portlet spec.).

        I've renamed this issue to more appropriately reflect this error and bumped its priority to Critical as well.

        I already have come up with a fix which passes the TCK (both tested against Pluto and Jetspeed 2.2.1) which I will commit shortly.
        Please check if it then works for you/uPortal too after which we can resolve this issue again.

        Show
        Ate Douma added a comment - Eric, I've now been able to reproduce the error: it is a bug in HttpServletPortletRequestWrapper indeed, method setupNestedDispatchPathValues() to be precise. This bug only shows up when going through multiple/nested dispatches like in your BookmarksPortlet but I also could easily reproduce it with using just two minimal servlets chaining a request dispatcher include call. Too bad the Portlet 2.0 TCK doesn't include tests for nested dispatching (which semantics is largely underspecified in the portlet spec.). I've renamed this issue to more appropriately reflect this error and bumped its priority to Critical as well. I already have come up with a fix which passes the TCK (both tested against Pluto and Jetspeed 2.2.1) which I will commit shortly. Please check if it then works for you/uPortal too after which we can resolve this issue again.
        Hide
        Eric Dalquist added a comment -

        I just realized the version of the WAR I put up depends on a local HSQL db running. I just uploaded a fixed version that uses an in-memory DB.

        Show
        Eric Dalquist added a comment - I just realized the version of the WAR I put up depends on a local HSQL db running. I just uploaded a fixed version that uses an in-memory DB.
        Hide
        Eric Dalquist added a comment -

        So I was able to reproduce this in Pluto using the following portlet: https://mywebspace.wisc.edu/dalquist/web/JA-SIG/PLUTO-590/BookmarksPortlet.war It's already setup for Pluto 2 so you should be able to drop it in and have it run.

        It renders two links:
        <link rel="stylesheet" href="/pluto/css/bookmarks.css" type="text/css"/>
        <script src="/pluto/script/bookmarks.min.js" type="text/javascript"></script>

        which are pointing to pluto and not to the portlet.

        What I'm wondering about is the portlet is using the Spring MVC framwork so there are actually three levels of request dispatching being done. The first from the portal to the PortletServlet the second from the Portlet impl to a servlet in the same context and a third from the servlet to a JSP.

        Here is the stack trace from the pluto code on.

        HttpServletPortletRequestWrapper.getContextPath() line: 980
        NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) line: not available [native method]
        NativeMethodAccessorImpl.invoke(Object, Object[]) line: 39
        DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: 25
        Method.invoke(Object, Object...) line: 597
        BeanELResolver.getValue(ELContext, Object, Object) line: 62
        CompositeELResolver.getValue(ELContext, Object, Object) line: 54
        AstValue.getValue(EvaluationContext) line: 123
        ValueExpressionImpl.getValue(ELContext) line: 186
        PageContextImpl.proprietaryEvaluate(String, Class, PageContext, ProtectedFunctionMapper, boolean) line: 935
        viewBookmarks.jsp line: 7
        viewBookmarks_jsp(HttpJspBase).service(HttpServletRequest, HttpServletResponse) line: 70
        viewBookmarks_jsp(HttpServlet).service(ServletRequest, ServletResponse) line: 717
        JspServletWrapper.service(HttpServletRequest, HttpServletResponse, boolean) line: 377
        JspServlet.serviceJspFile(HttpServletRequest, HttpServletResponse, String, Throwable, boolean) line: 313
        JspServlet.service(HttpServletRequest, HttpServletResponse) line: 260
        JspServlet(HttpServlet).service(ServletRequest, ServletResponse) line: 717
        ApplicationFilterChain.internalDoFilter(ServletRequest, ServletResponse) line: 290
        ApplicationFilterChain.doFilter(ServletRequest, ServletResponse) line: 206
        ApplicationDispatcher.invoke(ServletRequest, ServletResponse, ApplicationDispatcher$State) line: 646
        ApplicationDispatcher.doInclude(ServletRequest, ServletResponse) line: 551
        ApplicationDispatcher.include(ServletRequest, ServletResponse) line: 488
        PortletRequestDispatcherImpl.include(ServletRequest, ServletResponse) line: 268
        JstlView(InternalResourceView).renderMergedOutputModel(Map, HttpServletRequest, HttpServletResponse) line: 134
        JstlView(AbstractView).render(Map, HttpServletRequest, HttpServletResponse) line: 243
        ViewRendererServlet.renderView(HttpServletRequest, HttpServletResponse) line: 111
        ViewRendererServlet.processRequest(HttpServletRequest, HttpServletResponse) line: 84
        ViewRendererServlet.doGet(HttpServletRequest, HttpServletResponse) line: 65
        ViewRendererServlet(HttpServlet).service(HttpServletRequest, HttpServletResponse) line: 617
        ViewRendererServlet(HttpServlet).service(ServletRequest, ServletResponse) line: 717
        ApplicationFilterChain.internalDoFilter(ServletRequest, ServletResponse) line: 290
        ApplicationFilterChain.doFilter(ServletRequest, ServletResponse) line: 206
        ApplicationDispatcher.invoke(ServletRequest, ServletResponse, ApplicationDispatcher$State) line: 646
        ApplicationDispatcher.doInclude(ServletRequest, ServletResponse) line: 551
        ApplicationDispatcher.include(ServletRequest, ServletResponse) line: 488
        PortletRequestDispatcherImpl.doDispatch(PortletRequest, PortletResponse, boolean) line: 174
        PortletRequestDispatcherImpl.include(RenderRequest, RenderResponse) line: 227
        DispatcherPortlet.render(ModelAndView, RenderRequest, RenderResponse) line: 1077
        DispatcherPortlet.doRenderService(RenderRequest, RenderResponse) line: 809
        DispatcherPortlet(FrameworkPortlet).processRequest(PortletRequest, PortletResponse) line: 461
        DispatcherPortlet(FrameworkPortlet).doDispatch(RenderRequest, RenderResponse) line: 431
        DispatcherPortlet(GenericPortlet).render(RenderRequest, RenderResponse) line: 259
        FilterChainImpl.doFilter(RenderRequest, RenderResponse) line: 200
        FilterManagerImpl.processFilter(RenderRequest, RenderResponse, Portlet, PortletContext) line: 95
        PortletServlet.dispatch(HttpServletRequest, HttpServletResponse) line: 340
        PortletServlet.doGet(HttpServletRequest, HttpServletResponse) line: 261
        PortletServlet(HttpServlet).service(HttpServletRequest, HttpServletResponse) line: 617
        PortletServlet(HttpServlet).service(ServletRequest, ServletResponse) line: 717
        ApplicationFilterChain.internalDoFilter(ServletRequest, ServletResponse) line: 290
        ApplicationFilterChain.doFilter(ServletRequest, ServletResponse) line: 206
        ApplicationDispatcher.invoke(ServletRequest, ServletResponse, ApplicationDispatcher$State) line: 646
        ApplicationDispatcher.doInclude(ServletRequest, ServletResponse) line: 551
        ApplicationDispatcher.include(ServletRequest, ServletResponse) line: 488
        DefaultPortletInvokerService.invoke(PortletRequestContext, PortletRequest, PortletResponse, FilterManager, Integer) line: 233
        DefaultPortletInvokerService.render(PortletRequestContext, RenderRequest, RenderResponse, FilterManager) line: 117
        PortletContainerImpl.doRender(PortletWindow, HttpServletRequest, HttpServletResponse) line: 157

        Show
        Eric Dalquist added a comment - So I was able to reproduce this in Pluto using the following portlet: https://mywebspace.wisc.edu/dalquist/web/JA-SIG/PLUTO-590/BookmarksPortlet.war It's already setup for Pluto 2 so you should be able to drop it in and have it run. It renders two links: <link rel="stylesheet" href="/pluto/css/bookmarks.css" type="text/css"/> <script src="/pluto/script/bookmarks.min.js" type="text/javascript"></script> which are pointing to pluto and not to the portlet. What I'm wondering about is the portlet is using the Spring MVC framwork so there are actually three levels of request dispatching being done. The first from the portal to the PortletServlet the second from the Portlet impl to a servlet in the same context and a third from the servlet to a JSP. Here is the stack trace from the pluto code on. HttpServletPortletRequestWrapper.getContextPath() line: 980 NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) line: not available [native method] NativeMethodAccessorImpl.invoke(Object, Object[]) line: 39 DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: 25 Method.invoke(Object, Object...) line: 597 BeanELResolver.getValue(ELContext, Object, Object) line: 62 CompositeELResolver.getValue(ELContext, Object, Object) line: 54 AstValue.getValue(EvaluationContext) line: 123 ValueExpressionImpl.getValue(ELContext) line: 186 PageContextImpl.proprietaryEvaluate(String, Class, PageContext, ProtectedFunctionMapper, boolean) line: 935 viewBookmarks.jsp line: 7 viewBookmarks_jsp(HttpJspBase).service(HttpServletRequest, HttpServletResponse) line: 70 viewBookmarks_jsp(HttpServlet).service(ServletRequest, ServletResponse) line: 717 JspServletWrapper.service(HttpServletRequest, HttpServletResponse, boolean) line: 377 JspServlet.serviceJspFile(HttpServletRequest, HttpServletResponse, String, Throwable, boolean) line: 313 JspServlet.service(HttpServletRequest, HttpServletResponse) line: 260 JspServlet(HttpServlet).service(ServletRequest, ServletResponse) line: 717 ApplicationFilterChain.internalDoFilter(ServletRequest, ServletResponse) line: 290 ApplicationFilterChain.doFilter(ServletRequest, ServletResponse) line: 206 ApplicationDispatcher.invoke(ServletRequest, ServletResponse, ApplicationDispatcher$State) line: 646 ApplicationDispatcher.doInclude(ServletRequest, ServletResponse) line: 551 ApplicationDispatcher.include(ServletRequest, ServletResponse) line: 488 PortletRequestDispatcherImpl.include(ServletRequest, ServletResponse) line: 268 JstlView(InternalResourceView).renderMergedOutputModel(Map, HttpServletRequest, HttpServletResponse) line: 134 JstlView(AbstractView).render(Map, HttpServletRequest, HttpServletResponse) line: 243 ViewRendererServlet.renderView(HttpServletRequest, HttpServletResponse) line: 111 ViewRendererServlet.processRequest(HttpServletRequest, HttpServletResponse) line: 84 ViewRendererServlet.doGet(HttpServletRequest, HttpServletResponse) line: 65 ViewRendererServlet(HttpServlet).service(HttpServletRequest, HttpServletResponse) line: 617 ViewRendererServlet(HttpServlet).service(ServletRequest, ServletResponse) line: 717 ApplicationFilterChain.internalDoFilter(ServletRequest, ServletResponse) line: 290 ApplicationFilterChain.doFilter(ServletRequest, ServletResponse) line: 206 ApplicationDispatcher.invoke(ServletRequest, ServletResponse, ApplicationDispatcher$State) line: 646 ApplicationDispatcher.doInclude(ServletRequest, ServletResponse) line: 551 ApplicationDispatcher.include(ServletRequest, ServletResponse) line: 488 PortletRequestDispatcherImpl.doDispatch(PortletRequest, PortletResponse, boolean) line: 174 PortletRequestDispatcherImpl.include(RenderRequest, RenderResponse) line: 227 DispatcherPortlet.render(ModelAndView, RenderRequest, RenderResponse) line: 1077 DispatcherPortlet.doRenderService(RenderRequest, RenderResponse) line: 809 DispatcherPortlet(FrameworkPortlet).processRequest(PortletRequest, PortletResponse) line: 461 DispatcherPortlet(FrameworkPortlet).doDispatch(RenderRequest, RenderResponse) line: 431 DispatcherPortlet(GenericPortlet).render(RenderRequest, RenderResponse) line: 259 FilterChainImpl.doFilter(RenderRequest, RenderResponse) line: 200 FilterManagerImpl.processFilter(RenderRequest, RenderResponse, Portlet, PortletContext) line: 95 PortletServlet.dispatch(HttpServletRequest, HttpServletResponse) line: 340 PortletServlet.doGet(HttpServletRequest, HttpServletResponse) line: 261 PortletServlet(HttpServlet).service(HttpServletRequest, HttpServletResponse) line: 617 PortletServlet(HttpServlet).service(ServletRequest, ServletResponse) line: 717 ApplicationFilterChain.internalDoFilter(ServletRequest, ServletResponse) line: 290 ApplicationFilterChain.doFilter(ServletRequest, ServletResponse) line: 206 ApplicationDispatcher.invoke(ServletRequest, ServletResponse, ApplicationDispatcher$State) line: 646 ApplicationDispatcher.doInclude(ServletRequest, ServletResponse) line: 551 ApplicationDispatcher.include(ServletRequest, ServletResponse) line: 488 DefaultPortletInvokerService.invoke(PortletRequestContext, PortletRequest, PortletResponse, FilterManager, Integer) line: 233 DefaultPortletInvokerService.render(PortletRequestContext, RenderRequest, RenderResponse, FilterManager) line: 117 PortletContainerImpl.doRender(PortletWindow, HttpServletRequest, HttpServletResponse) line: 157
        Hide
        Ate Douma added a comment -

        Hmm, I just tested this out by adding <%= request.getContextPath() %> to testsuite/jsp/edit.jsp and switched the testsuite portlet to edit mode.
        Output: /testsuite
        So, I'm puzzled how to reproduce this problem as it works for me...

        Luis or Eric, can either of you elaborate how/when the problem arises or can be reproduced?

        Show
        Ate Douma added a comment - Hmm, I just tested this out by adding <%= request.getContextPath() %> to testsuite/jsp/edit.jsp and switched the testsuite portlet to edit mode. Output: /testsuite So, I'm puzzled how to reproduce this problem as it works for me... Luis or Eric, can either of you elaborate how/when the problem arises or can be reproduced?
        Hide
        Ate Douma added a comment - - edited

        I have to rollback this change as it breaks Portlet 2.0 TCK tests (2x).

        The solution isn't and cannot be as simple as just returning the current underlying portlet contextpath: the portlet spec is to particular in this case (and for related request path methods).
        The "problem" lies in the fact that the portlet spec demands very specific semantics for especially request forward handling which don't "fit" with the servlet container semantics.
        In addition, named servlet dispatching is even more peculiar and different from normal servlet container semantics.
        For all these request dispatching, the servlet container (and thus servlet spec) only has to concern itself with an incoming "client" request coming directly from the user/browser.
        However, with the portlet spec, the portal plays an intermediate role in this, causing the servlet semantics no longer to hold.

        This is the reason why the HttpServletPortletRequestWrapper logic is, and is required to be, so "complicated", as even while the portlet spec imposes deviating semantics, the target/invoked servlet still must be provided with a consistent request "context" from the servlet container point of view as well.

        Anyway, it might be true that the current implementation does not yet properly deal with the use-case presented by this issue (I haven't actually tested/verified yet), in which case it needs fixing for sure.
        But possibly the error might also be somewhere else, maybe within the Pluto Portal Driver itself (in Jetspeed which also uses the Pluto container I haven't encountered this issue).

        I will look into this issue shortly and try to come up with a proper solution. If the problem indeed is within the container, it'll have to pass the Portlet 2.0 TCK to qualify of course.

        Show
        Ate Douma added a comment - - edited I have to rollback this change as it breaks Portlet 2.0 TCK tests (2x). The solution isn't and cannot be as simple as just returning the current underlying portlet contextpath: the portlet spec is to particular in this case (and for related request path methods). The "problem" lies in the fact that the portlet spec demands very specific semantics for especially request forward handling which don't "fit" with the servlet container semantics. In addition, named servlet dispatching is even more peculiar and different from normal servlet container semantics. For all these request dispatching, the servlet container (and thus servlet spec) only has to concern itself with an incoming "client" request coming directly from the user/browser. However, with the portlet spec, the portal plays an intermediate role in this, causing the servlet semantics no longer to hold. This is the reason why the HttpServletPortletRequestWrapper logic is, and is required to be, so "complicated", as even while the portlet spec imposes deviating semantics, the target/invoked servlet still must be provided with a consistent request "context" from the servlet container point of view as well. Anyway, it might be true that the current implementation does not yet properly deal with the use-case presented by this issue (I haven't actually tested/verified yet), in which case it needs fixing for sure. But possibly the error might also be somewhere else, maybe within the Pluto Portal Driver itself (in Jetspeed which also uses the Pluto container I haven't encountered this issue). I will look into this issue shortly and try to come up with a proper solution. If the problem indeed is within the container, it'll have to pass the Portlet 2.0 TCK to qualify of course.
        Hide
        Eric Dalquist added a comment -

        Fixed in trunk and 2.0.x by delegating the getContextPath call to PortletRequest

        Show
        Eric Dalquist added a comment - Fixed in trunk and 2.0.x by delegating the getContextPath call to PortletRequest
        Hide
        Eric Dalquist added a comment -

        It looks like this is a bug in pluto per PLT.19.4.4 of the portlet 2.0 spec which states:

        The following methods of the HttpServletRequest must be equivalent to the methods of the PortletRequest of similar name: getScheme, getServerName, getServerPort, getAttribute, getAttributeNames, setAttribute, removeAttribute, getLocale, getLocales, isSecure, getAuthType, getContextPath, getRemoteUser, getUserPrincipal, getRequestedSessionId, isRequestedSessionIdValid, getCookies

        Show
        Eric Dalquist added a comment - It looks like this is a bug in pluto per PLT.19.4.4 of the portlet 2.0 spec which states: The following methods of the HttpServletRequest must be equivalent to the methods of the PortletRequest of similar name: getScheme, getServerName, getServerPort, getAttribute, getAttributeNames, setAttribute, removeAttribute, getLocale, getLocales, isSecure, getAuthType, getContextPath, getRemoteUser, getUserPrincipal, getRequestedSessionId, isRequestedSessionIdValid, getCookies

          People

          • Assignee:
            Ate Douma
            Reporter:
            Luis
          • Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved:

              Development