PrimeFaces 6.0 does not cache images on the client side

Given a <p:dataTable> rendering images in one of the columns.

<p:dataTable id="dataTable" var="row" value="#{bean}"
             lazy="true"
             skipChildren="true">

    <p:column headerText="Image">
        <p:cellEditor>
            <f:facet name="output">
                <p:graphicImage value="#{imageBean.image}" stream="true" cache="true">
                    <f:param name="id" value="#{row.id}"/>
                    <f:param name="width" value="100"/>
                    <f:param name="height" value="100"/>
                </p:graphicImage>
            </f:facet>

            <f:facet name="input">
                <p:graphicImage value="#{imageBean.image}" stream="true" cache="true">
                    <f:param name="id" value="#{row.id}"/>
                    <f:param name="width" value="100"/>
                    <f:param name="height" value="100"/>
                </p:graphicImage>

                <!-- <p:overlayPanel> here for file upload -->
            </f:facet>
        </p:cellEditor>
    </p:column>

    <p:column headerText="Edit">
        <p:rowEditor/>
    </p:column>
</p:dataTable>

The data table may contain other essential commonly used attributes and columns as and when needed.

When this table is (Ajaxically) updated, all images are fetched from the database (or disk file system, if used) as if they are not cached by the browser at all even though cache is explicitly set to true (which is the default value). This was working well previoulsly with PrimeFaces 5.3 final.

The migration guide states nothing about it but apparently something has been changed about caching <p:graphicImage>.

Any suggestion to fix the problem?

In the example above, if the table contains 5 images in 5 rows, for example, the database will be queried 10 times on every single update made to the <p:dataTable> (except inline row editing which defaults to the current row) which should not happen as getting images especially from a database is very costly.


Request / response headers using PrimeFaces 6.0 final (running on WildFly 10.0.0 final), when an initial request is made to the server to serve an image (does not work - images are not cached).

General
    Request URL:https://localhost:8443/ContextRoot/javax.faces.resource/dynamiccontent.properties.xhtml?ln=primefaces&v=6.0&pfdrid=aed903cc-daba-4822-a62b-888b9a0ef2ac&pfdrt=sc&id=14&width=100&height=100&pfdrid_c=true
    Request Method:GET
    Status Code:200 OK
    Remote Address:127.0.0.1:8443
Response Headers
    Cache-Control:max-age=29030400
    Connection:keep-alive
    Date:Sat, 23 Jul 2016 06:59:54 GMT
    Expires:Sun, 23 Jul 2017 06:59:54 GMT
    Server:WildFly/10
    Transfer-Encoding:chunked
    X-Powered-By:Undertow/1
Request Headers
    Accept:image/webp,image/*,*/*;q=0.8
    Accept-Encoding:gzip, deflate, sdch
    Accept-Language:en-US,en;q=0.8
    Connection:keep-alive
    Cookie:JSESSIONID=4AoRGa1IAPTB4KssnikbO9uUetcQpMupli8BkGga.om-f6b0ea3ad206; __utma=111872281.616526714.1454485589.1468749319.1468751735.4; __utmz=111872281.1454485589.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none)
    Host:localhost:8443
    Referer:https://localhost:8443/ContextRoot/admin/Brand
    User-Agent:Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.112 Safari/537.36
Query String Parameters
    ln:primefaces
    v:6.0
    pfdrid:aed903cc-daba-4822-a62b-888b9a0ef2ac
    pfdrt:sc
    id:14
    width:100
    height:100
    pfdrid_c:true

Request / response headers using PrimeFaces 5.3 final (running on GlassFish 4.1), when an initial request is made to the server to serve an image (works as intended - images are cached).

General
    Request URL:https://localhost:8181/ContextRoot/javax.faces.resource/dynamiccontent.properties.xhtml?ln=primefaces&v=5.3&pfdrid=aAPHlxcQ2lcqfvzacYoCC6iUxLU1VVFp&pfdrt=sc&id=11&width=100&height=100&pfdrid_c=true
    Request Method:GET
    Status Code:200 OK
    Remote Address:127.0.0.1:8181
Response Headers
    Cache-Control:max-age=29030400
    Date:Sat, 23 Jul 2016 07:15:03 GMT
    Expires:Sun, 23 Jul 2017 07:15:04 GMT
    Pragma:No-cache
    Server:GlassFish Server Open Source Edition  4.1
    Transfer-Encoding:chunked
    X-Powered-By:Servlet/3.1 JSP/2.3 (GlassFish Server Open Source Edition  4.1  Java/Oracle Corporation/1.8)
Request Headers
    Accept:image/webp,image/*,*/*;q=0.8
    Accept-Encoding:gzip, deflate, sdch
    Accept-Language:en-US,en;q=0.8
    Connection:keep-alive
    Cookie:JSESSIONID=69b5070218cfe0fc6eaac2141c13; __utma=111872281.616526714.1454485589.1468749319.1468751735.4; __utmz=111872281.1454485589.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none)
    Host:localhost:8181
    Referer:https://localhost:8181/ContextRoot/admin/Brand
    User-Agent:Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.112 Safari/537.36
Query String Parameters
    ln:primefaces
    v:5.3
    pfdrid:aAPHlxcQ2lcqfvzacYoCC6iUxLU1VVFp
    pfdrt:sc
    id:11
    width:100
    height:100
    pfdrid_c:true


ANSWERS:


Headers look good. This suggests that something in query string parameters changes from request to request. This would also be interpreted as a brand new resource and thus bust the caching even though the base URI (the part before URL query string separator ?) is exactly the same.

And indeed, PrimeFaces 6.0 has changed the way how the pfdrid query string parameter is generated. It has become a completely random UUID which changes every time when the HTML <img src> is rendered. See also line 59 of PF 6.0 source code. In PrimeFaces 5.3, it was encrypted based on EL expression string and thus guaranteed to be the same across requests as long as the EL expression string is the same. See also line 53 of PF 5.3 source code.

The change was introduced by Cagatay without a reference to an issue ticket. So it remains unclear why exactly this change was done. But after all it doesn't offer the client the opportunity anymore to cache the dynamic content and would thus actually decrease performance in both ends. This is definitely worth an issue ticket at PrimeFaces, so I created one: issue 1765.

I'm not seeing a clean way to solve this other than hacking the PrimeFaces source code. Your best bet is to replace the <p:graphicImage> by a <h:graphicImage> with a "plain vanilla servlet", or if you happen to use JSF utility library OmniFaces, then the <o:graphicImage> with a simple bean. Those approaches are already detailed in this related Q&A: Show image as byte[] from database as graphic image in JSF page.


Update: as per issue 1765, it has been fixed for PrimeFaces 6.1.



 MORE:


 ? Using JSF for <a href="myPicture.png" .../>
 ? Prevent o:graphicImage from throwing an exception, when parameters being passed to a target getter method get mismatched in number, order and type
 ? Display dynamic image from database with p:graphicImage and StreamedContent
 ? Display dynamic image from database with p:graphicImage and StreamedContent
 ? Display dynamic image from database with p:graphicImage and StreamedContent
 ? Display database blob images in <p:graphicImage> inside <ui:repeat>
 ? Open file browse panel of p:fileUpload on click of an image
 ? JSF composite component childrens
 ? JSF Composite component and selectItems
 ? JSF Recursive Composite Component