ICEfaces
  1. ICEfaces
  2. ICE-8540

ACE Data Exporter components fail on Liferay when request is not localhost

    Details

    • Type: Bug Bug
    • Status: Closed
    • Priority: Major Major
    • Resolution: Fixed
    • Affects Version/s: EE-3.0.0.GA, EE-3.0.0.GA_P01
    • Fix Version/s: EE-3.0.0.GA_P01, 3.2
    • Component/s: ACE-Components, Sample Apps
    • Labels:
      None
    • Environment:
      Liferay Portal Portlet ACE Data Exporter Component

      Description

      In testing the showcase-portlet examples on Liferay (any of 5.2.3, 6.0, 6.1), the following behaviour was observed when using IE. It exists in the EE-3.0.0.GA release as well as on the current maintenance branch.

      When running any of the Data Exporter examples, after clicking the "Export File" button, the operation appears to simply silently fail. There were no client or server-side errors detected.

        Activity

        Hide
        Deryk Sinotte added a comment -

        Looks like it's not an IE problem specifically. The reason it only showed up on IE is that it was the only browser that I was testing with that wasn't using http://localhost:8080. Running Chrome from a remote machine shows the same issue.

        The problem seems to stem from the initial update that comes back. When you go to the initial portal page from a browser on a different machine, the initial address might look something like:

        http://myRemoteHostAddress:8080/web/guest/exporter

        When you click the export button, the URL for the request looks something like:

        http://myRemoteHostAddress:8080/web/guest/exporter?_dataExporter_WAR_showcaseportlet__facesViewId=%2Fportlet-view.xhtml&p_p_col_count=1&p_p_col_id=column-2&p_p_id=dataExporter_WAR_showcaseportlet&p_p_lifecycle=2

        The response that comes back includes a script that needs to be run to actually fetch the resource. It looks like this (formatted a bit for readability):

        <update id="A9102:form1:dataExporter_script">
        <![CDATA[<span id="A9102:form1:dataExporter_script">
        <script type="text/javascript">
        ice.ace.jq(ice.ace.escapeClientId('A9102:form1:dataExporter')).button();
        if (ice.ace.DataExporters['A9102:form1:dataExporter'])
        ice.ace.DataExporters['A9102:form1:dataExporter'].url('http://localhost:8080/web/guest/exporter?_dataExporter_WAR_showcaseportlet_javax.faces.resource=s8040c1d4-6309-4ee5-8f0d-a44cf4bae1a7&p_p_col_count=1&p_p_col_id=column-2&p_p_id=dataExporter_WAR_showcaseportlet&p_p_lifecycle=2');

        </script>
        </span>]]>
        </update>

        The interesting part to note is the URL it's constructed that contains the resource id it'll be looking for. It starts with "http://localhost:8080/web/guest/exporter?...". So it's trying to make a request to localhost even though the original request was not localhost. This explains why it works when testing it locally.

        Show
        Deryk Sinotte added a comment - Looks like it's not an IE problem specifically. The reason it only showed up on IE is that it was the only browser that I was testing with that wasn't using http://localhost:8080 . Running Chrome from a remote machine shows the same issue. The problem seems to stem from the initial update that comes back. When you go to the initial portal page from a browser on a different machine, the initial address might look something like: http://myRemoteHostAddress:8080/web/guest/exporter When you click the export button, the URL for the request looks something like: http://myRemoteHostAddress:8080/web/guest/exporter?_dataExporter_WAR_showcaseportlet__facesViewId=%2Fportlet-view.xhtml&p_p_col_count=1&p_p_col_id=column-2&p_p_id=dataExporter_WAR_showcaseportlet&p_p_lifecycle=2 The response that comes back includes a script that needs to be run to actually fetch the resource. It looks like this (formatted a bit for readability): <update id="A9102:form1:dataExporter_script"> <![CDATA[<span id="A9102:form1:dataExporter_script"> <script type="text/javascript"> ice.ace.jq(ice.ace.escapeClientId('A9102:form1:dataExporter')).button(); if (ice.ace.DataExporters ['A9102:form1:dataExporter'] ) ice.ace.DataExporters ['A9102:form1:dataExporter'] .url('http://localhost:8080/web/guest/exporter?_dataExporter_WAR_showcaseportlet_javax.faces.resource=s8040c1d4-6309-4ee5-8f0d-a44cf4bae1a7&p_p_col_count=1&p_p_col_id=column-2&p_p_id=dataExporter_WAR_showcaseportlet&p_p_lifecycle=2'); </script> </span>]]> </update> The interesting part to note is the URL it's constructed that contains the resource id it'll be looking for. It starts with "http://localhost:8080/web/guest/exporter?...". So it's trying to make a request to localhost even though the original request was not localhost. This explains why it works when testing it locally.
        Hide
        Deryk Sinotte added a comment -

        Editing description to better reflect actual issue.

        Show
        Deryk Sinotte added a comment - Editing description to better reflect actual issue.
        Hide
        Deryk Sinotte added a comment -

        Turns out you can more easily reproduce the problem simply by making the first portlet export a file from one host (e.g. http://localhost:8080/) and then a subsequent export using a different host (e.g. http://192.168.5.1:8080). They can even be on the same machine.

        In ResourceRegistry.addResource(), there is the following bit of logic:

        ...
        String[] pathTemplate = EnvUtils.getPathTemplate();
        String path = pathTemplate[0] + key + pathTemplate[1];
        path = FacesContext.getCurrentInstance().getExternalContext().encodeResourceURL(path);
        return path;

        The path template provides a way to get the proper bits for building up a valid URL with the key being the id of resource in the registry. The entire path is then encoded and returned.

        The EnvUtils.getPathTemplate() does the following:

        public static String[] getPathTemplate() {
        FacesContext facesContext = FacesContext.getCurrentInstance();
        Map applicationMap = facesContext.getExternalContext()
        .getApplicationMap();
        String[] pathTemplate = (String[]) applicationMap.get(PATH_TEMPLATE);
        ...

        First it checks to see if a path template already exists and uses that. In our testing with portlets, if you first export some files from "localhost", that's what gets stored in the application map as the path template and used from then on.

        The reason it doesn't fail in non-portlet mode is that the path template ends up being a relative path (no host or port information). But with portlets, the entire URL is used as a template, leading to failures when subsequent requests come from a different host.

        In other words, when getPathTemplate() generates a path using a "dummy" resource:

        PORTLET: http://localhost:8080/web/guest/exporter?_dataExporter_WAR_showcaseportlet_javax.faces.resource=bridge.js&p_p_col_count=1&p_p_col_id=column-2&p_p_id=dataExporter_WAR_showcaseportlet&p_p_lifecycle=2

        NON-PORTLET:
        /showcase/javax.faces.resource/bridge.js.jsf

        The likely fix is to disable the optimization for storing the path template in the application map when running in a portal.

        Show
        Deryk Sinotte added a comment - Turns out you can more easily reproduce the problem simply by making the first portlet export a file from one host (e.g. http://localhost:8080/ ) and then a subsequent export using a different host (e.g. http://192.168.5.1:8080 ). They can even be on the same machine. In ResourceRegistry.addResource(), there is the following bit of logic: ... String[] pathTemplate = EnvUtils.getPathTemplate(); String path = pathTemplate [0] + key + pathTemplate [1] ; path = FacesContext.getCurrentInstance().getExternalContext().encodeResourceURL(path); return path; The path template provides a way to get the proper bits for building up a valid URL with the key being the id of resource in the registry. The entire path is then encoded and returned. The EnvUtils.getPathTemplate() does the following: public static String[] getPathTemplate() { FacesContext facesContext = FacesContext.getCurrentInstance(); Map applicationMap = facesContext.getExternalContext() .getApplicationMap(); String[] pathTemplate = (String[]) applicationMap.get(PATH_TEMPLATE); ... First it checks to see if a path template already exists and uses that. In our testing with portlets, if you first export some files from "localhost", that's what gets stored in the application map as the path template and used from then on. The reason it doesn't fail in non-portlet mode is that the path template ends up being a relative path (no host or port information). But with portlets, the entire URL is used as a template, leading to failures when subsequent requests come from a different host. In other words, when getPathTemplate() generates a path using a "dummy" resource: PORTLET: http://localhost:8080/web/guest/exporter?_dataExporter_WAR_showcaseportlet_javax.faces.resource=bridge.js&p_p_col_count=1&p_p_col_id=column-2&p_p_id=dataExporter_WAR_showcaseportlet&p_p_lifecycle=2 NON-PORTLET: /showcase/javax.faces.resource/bridge.js.jsf The likely fix is to disable the optimization for storing the path template in the application map when running in a portal.
        Hide
        Deryk Sinotte added a comment -

        Checked in a fix to both the maintenance branch and the trunk that avoids caching the path of the dummy resource when running in a portal. The optimization is still there for non-portal platforms but with portals the path will be calculated for each resource.

        Show
        Deryk Sinotte added a comment - Checked in a fix to both the maintenance branch and the trunk that avoids caching the path of the dummy resource when running in a portal. The optimization is still there for non-portal platforms but with portals the path will be calculated for each resource.

          People

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

            Dates

            • Created:
              Updated:
              Resolved: