ICEfaces
  1. ICEfaces
  2. ICE-6929

FacesContext.externalContext.response.namespace resets to empty after server roundtrip

    Details

    • Type: Bug Bug
    • Status: Closed
    • Priority: Major Major
    • Resolution: Fixed
    • Affects Version/s: 1.8.2-EE-GA_P02
    • Fix Version/s: EE-1.8.2.GA_P04
    • Component/s: Framework
    • Labels:
      None
    • Environment:
      Liferay 5.2.3 + Tomcat 6
    • Affects:
      Documentation (User Guide, Ref. Guide, etc.), Compatibility/Configuration
    • Workaround Exists:
      Yes
    • Workaround Description:
      Hide
      Use namespace stored as request attribute:


          public String getNamespace() {
              Map requestMap = FacesContext.getCurrentInstance().getExternalContext().getRequestMap();
              Object namespace = requestMap.get("com.icesoft.faces.NAMESPACE");
              if( namespace != null ){
                  return namespace.toString();
              }
              return null;
          }
      Show
      Use namespace stored as request attribute:     public String getNamespace() {         Map requestMap = FacesContext.getCurrentInstance().getExternalContext().getRequestMap();         Object namespace = requestMap.get("com.icesoft.faces.NAMESPACE");         if( namespace != null ){             return namespace.toString();         }         return null;     }

      Description

      This looks to be a regression from P01. The user has facesContext.externalContext.response.namespace in their page level EL expression and on first render is correctly set. The issue occurs after a server round trip where the value then becomes empty. To reproduce the issue in the attached portal test case, select the text inside of the inputTtext and hit enter. The label for the facesContext.externalContext.response.namespace becomes blank. Here is the snippet of code:

      <ice:outputLabel value="#{facesContext.externalContext.response.namespace}" />

      In a partial update (button click or hitting enter in an inputText) in P02, the PortletResponse acquired using the property response of the externalContext is null. However in P01, the PortletResponse is correctly set.

      Test case is intended for deployment on Liferay 5.2.3 + Tomcat 6 and can be found under the "Test" portlet category.

        Activity

        Hide
        Deryk Sinotte added a comment -

        I have confirmed the regression is between P01 and P02 and was introduced as part of this change:

        http://jira.icefaces.org/browse/ICE-6197

        This JIRA was to merge in substantial patches we'd made against P01 for other customers. The specific change in this case that is causing this problem relates to the RenderResponse reference. In the PortletExternalContext (our version of the JSF ExternalContext for portlet environments) was maintaining a reference to the original RenderResponse between Ajax requests. This was causing a significant memory leak during load testing and could only be solved by releasing the reference to the RenderResponse when the FacesContext.release() method was called. The side-effect is the regression where calling PortletExternalContext.getResponse() now returns null after the initial page request.

        There is a workaround as we do store the namespace as a request attribute and this can be retrieved. Unfortunately, the strategy of using more direct EL syntax does not work:

        <ice:outputText value="Namespace: #

        {facesContext.externalContext.response.namespace}

        "/>

        But it is possible to use some simple backing bean code and modify the EL to something like the following. Assume a managed bean called "Simple" that has the following method:

        public String getNamespace() {
        Map requestMap = FacesContext.getCurrentInstance().getExternalContext().getRequestMap();
        Object namespace = requestMap.get("com.icesoft.faces.NAMESPACE");
        if( namespace != null )

        { return namespace.toString(); }

        return null;
        }

        then the following EL can be used to get the namespace which will survive multiple Ajax requests:

        <ice:outputText value="Namespace alternative: #

        {simple.namespace}

        "/>

        I've verified that this works against the test case. However, the response not being available may be considered a more serious issue and to fix it will require some effort in order not to re-introduce the memory issues that were originally addressed.

        Show
        Deryk Sinotte added a comment - I have confirmed the regression is between P01 and P02 and was introduced as part of this change: http://jira.icefaces.org/browse/ICE-6197 This JIRA was to merge in substantial patches we'd made against P01 for other customers. The specific change in this case that is causing this problem relates to the RenderResponse reference. In the PortletExternalContext (our version of the JSF ExternalContext for portlet environments) was maintaining a reference to the original RenderResponse between Ajax requests. This was causing a significant memory leak during load testing and could only be solved by releasing the reference to the RenderResponse when the FacesContext.release() method was called. The side-effect is the regression where calling PortletExternalContext.getResponse() now returns null after the initial page request. There is a workaround as we do store the namespace as a request attribute and this can be retrieved. Unfortunately, the strategy of using more direct EL syntax does not work: <ice:outputText value="Namespace: # {facesContext.externalContext.response.namespace} "/> But it is possible to use some simple backing bean code and modify the EL to something like the following. Assume a managed bean called "Simple" that has the following method: public String getNamespace() { Map requestMap = FacesContext.getCurrentInstance().getExternalContext().getRequestMap(); Object namespace = requestMap.get("com.icesoft.faces.NAMESPACE"); if( namespace != null ) { return namespace.toString(); } return null; } then the following EL can be used to get the namespace which will survive multiple Ajax requests: <ice:outputText value="Namespace alternative: # {simple.namespace} "/> I've verified that this works against the test case. However, the response not being available may be considered a more serious issue and to fix it will require some effort in order not to re-introduce the memory issues that were originally addressed.
        Hide
        Deryk Sinotte added a comment -

        As part of the work for case http://jira.icesoft.org/browse/ICE-6197, we ported over some portlet optimizations that fixed memory leaks. One of these optimizations was to null out the reference to the PortletResponse when the PortletExternalContext was released.

        The upside was that it got rid of some serious leaks. The side-effect was that, with extended request scope, we no longer had a PortletResponse to query. Which brings us to this JIRA.

        Because of extended request scope and the way of handling portlet requests in ICEfaces 1.x, all the subsequent Ajax requests from the portlet no longer have a valid response object. In other words the following returns a valid PortletResponse only during the initial render and returns null for subsequent Ajax calls:

        Object obj = FacesContext.getCurrentInstance().getExternalContext().getResponse();

        For the most part this isn't a massive problem as we don't recommend working directly with the portlet response anyway, instead choosing to use ExternalContext API only. Of course the best laid plans....

        This change to release the response reference was made between P01 and P02 and caused a regression with the customer's app. We provided some workaround code that helped out in the interim. To "really" fix this properly is beyond the scope of work for the product at this point. So instead, when someone calls ExternalContext.getResponse():

        public Object getResponse() {
        if(response != null)

        { return response; }

        //ICE-6929: As part of the fix for ICE-6197, we released the response reference in order to
        // avoid memory leaks but that means that ExternalContext.getResponse() will return
        // null and can't be used. Here we provide a proxy that is mostly unimplemented except
        // specific methods required to help with bugs.
        boolean useProxyPortletResponse = configuration.getAttributeAsBoolean("useProxyPortletResponse", true);
        if(useProxyPortletResponse)

        { return new ProxyPortletResponse(getOriginalResponse()); }

        return null;
        }

        If the response is no longer valid (for all those Ajax requests in extended request scope) we return a ProxyPortletResponse instance. This is kind of similar to the technique we use newer versions of ICEfaces. For almost all the methods in the proxy, we return an UnsupportedOperationException:

        public PortletURL createActionURL()

        { log.error(UNSUPPORTED_MESSAGE); throw new UnsupportedOperationException(); }

        For the getNamespace() method, we use the workaround code as noted before in the description:

        public String getNamespace() {
        Map requestMap = FacesContext.getCurrentInstance().getExternalContext().getRequestMap();
        Object namespace = requestMap.get("com.icesoft.faces.NAMESPACE");
        if (namespace != null)

        { return (String) namespace; }

        return null;
        }

        Note that this new proxy behaviour is active by default but that there is a context parameter to turn it off:

        <context-param>
        <param-name>com.icesoft.faces.useProxyPortletResponse</param-name>
        <param-value>false</param-value>
        </context-param>

        This is provided in case application developer's code relies on checking for the response being null:

        if( response == null)...

        which could have undesirable side-effects. It's possible that more of the proxy methods could be implemented as required in the future.

        Show
        Deryk Sinotte added a comment - As part of the work for case http://jira.icesoft.org/browse/ICE-6197 , we ported over some portlet optimizations that fixed memory leaks. One of these optimizations was to null out the reference to the PortletResponse when the PortletExternalContext was released. The upside was that it got rid of some serious leaks. The side-effect was that, with extended request scope, we no longer had a PortletResponse to query. Which brings us to this JIRA. Because of extended request scope and the way of handling portlet requests in ICEfaces 1.x, all the subsequent Ajax requests from the portlet no longer have a valid response object. In other words the following returns a valid PortletResponse only during the initial render and returns null for subsequent Ajax calls: Object obj = FacesContext.getCurrentInstance().getExternalContext().getResponse(); For the most part this isn't a massive problem as we don't recommend working directly with the portlet response anyway, instead choosing to use ExternalContext API only. Of course the best laid plans.... This change to release the response reference was made between P01 and P02 and caused a regression with the customer's app. We provided some workaround code that helped out in the interim. To "really" fix this properly is beyond the scope of work for the product at this point. So instead, when someone calls ExternalContext.getResponse(): public Object getResponse() { if(response != null) { return response; } // ICE-6929 : As part of the fix for ICE-6197, we released the response reference in order to // avoid memory leaks but that means that ExternalContext.getResponse() will return // null and can't be used. Here we provide a proxy that is mostly unimplemented except // specific methods required to help with bugs. boolean useProxyPortletResponse = configuration.getAttributeAsBoolean("useProxyPortletResponse", true); if(useProxyPortletResponse) { return new ProxyPortletResponse(getOriginalResponse()); } return null; } If the response is no longer valid (for all those Ajax requests in extended request scope) we return a ProxyPortletResponse instance. This is kind of similar to the technique we use newer versions of ICEfaces. For almost all the methods in the proxy, we return an UnsupportedOperationException: public PortletURL createActionURL() { log.error(UNSUPPORTED_MESSAGE); throw new UnsupportedOperationException(); } For the getNamespace() method, we use the workaround code as noted before in the description: public String getNamespace() { Map requestMap = FacesContext.getCurrentInstance().getExternalContext().getRequestMap(); Object namespace = requestMap.get("com.icesoft.faces.NAMESPACE"); if (namespace != null) { return (String) namespace; } return null; } Note that this new proxy behaviour is active by default but that there is a context parameter to turn it off: <context-param> <param-name>com.icesoft.faces.useProxyPortletResponse</param-name> <param-value>false</param-value> </context-param> This is provided in case application developer's code relies on checking for the response being null: if( response == null)... which could have undesirable side-effects. It's possible that more of the proxy methods could be implemented as required in the future.
        Hide
        Deryk Sinotte added a comment -

        Committed a fix that allows a proxy response to be created once the original response has been released. This is constrained to PortletExternalContext only and the proxy is mostly unimplemented except for getNamespace(). I tested against the test case in this JIRA as well as the Component Showcase and didn't see any side-effects.

        I also tested the context parameter to ensure that setting it to false it has the desired effect of not creating/using the proxy. While doing that I made a slight adjustment to the name of the parameter to make is consistent with our other portlet-specific context parameters:

        <context-param>
        <param-name>com.icesoft.faces.portlet.useProxyPortletResponse</param-name>
        <param-value>false</param-value>
        </context-param>

        Show
        Deryk Sinotte added a comment - Committed a fix that allows a proxy response to be created once the original response has been released. This is constrained to PortletExternalContext only and the proxy is mostly unimplemented except for getNamespace(). I tested against the test case in this JIRA as well as the Component Showcase and didn't see any side-effects. I also tested the context parameter to ensure that setting it to false it has the desired effect of not creating/using the proxy. While doing that I made a slight adjustment to the name of the parameter to make is consistent with our other portlet-specific context parameters: <context-param> <param-name>com.icesoft.faces.portlet.useProxyPortletResponse</param-name> <param-value>false</param-value> </context-param>

          People

          • Assignee:
            Deryk Sinotte
            Reporter:
            Tyler Johnson
          • Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved: