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.
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.