Error in streaming dynamic resource. null

18,676

Solution 1

This is a bug (at least, a functional/technical design mistake) in PrimeResourceHandler. It shouldn't have assumed the dynamic resource or its content to be never null. It should have conditionally checked if that was the case and then simply have returned a HTTP 404 "Not Found" response.

In other words, instead of

85    streamedContent = (StreamedContent) ve.getValue(eLContext);
86 
87    externalContext.setResponseStatus(200);
88    externalContext.setResponseContentType(streamedContent.getContentType());

they should have done

85    streamedContent = (StreamedContent) ve.getValue(eLContext);
86 
87    if (streamedContent == null || streamedContent.getStream() == null) {
88        externalContext.responseSendError(HttpServletResponse.SC_NOT_FOUND, ((HttpServletRequest) externalContext.getRequest()).getRequestURI());
89        return;
90    }
91
92    externalContext.setResponseStatus(200);
93    externalContext.setResponseContentType(streamedContent.getContentType());

This way you can just return null or an empty StreamedContent from the getImage() method in order to generate a decent 404.

Well, what can you do?

  1. Report it to them and hope that they'll fix it. Update They fixed it in version 5.2.

  2. And/or, put a copy of PrimeResourceHandler class in the Java source folder of your webapp project, in exactly its own org.primefaces.application package and then just edit it to include the mentioned change and finally just build/deploy your webapp project as WAR as usual. Classes in /WEB-INF/classes have higher classloading precedence over those in JARs in /WEB-INF/lib, so the modified one will be used instead.

Solution 2

I would suggest to include an attribute in the object the <p:dataTable> is iterating on to signify if an image exists. That way, there are no unnecessary (or null return) calls to BannerBean.getImage().

Example:

<p:column headerText="Header">
    <p:graphicImage value="#{bannerBean.image}" height="200" width="200"
            rendered="#{row.hasImage}">
        <f:param name="id" value="#{row.bannerId}"/>
    </p:graphicImage>
<p:column>

Another option is to grab the Primefaces source code, edit PrimeResourceHandler.java, and build it. (see the wiki)


An alternative solution would be to setup your own Serlvet to provide the images.

  • Main benefit is that browsers can cache the image (you can specify cache timeout if wanted).
  • Be aware of SQL Injection & other security attacks.
  • Attach some sort of login/permission check for additional security. (if needed)

Example:

<p:column headerText="Header">
    <p:graphicImage value="#{request.contextPath}/images/banner/?id=#{row.bannerId}"
            height="200" width="200" />
<p:column>
@WebServlet(name = "Retrieve Banner Images", urlPatterns = "/images/banner/*")
public class BannerImageServlet extends HttpServlet
{
    @EJB
    private final BannerBeanLocal service;

    protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
        String[] ids = req.getParameterValues("id");
        if(ids != null && ids.length == 1) {
            byte[] bytes = service.findImageById(Long.parseLong(ids[0]));
            if(bytes != null) {
                // see link #3 below
            }
        }
    }
}

Sources / useful links:

  1. #{request.contextPath}
  2. @WebServlet tutorial
  3. how to send byte stream back to the client
Share:
18,676
Tiny
Author by

Tiny

Just an orphan kid and have no more to say. Three things in general, cannot be avoided (at least I can never) Mother Mother-tongue Mother-land. They are always unique. I'm a family-less boy. My family was hunted leaving me all alone when my house targeted and deliberately set on a fire by a mob during a nonsense communal riot but I was survived by a rescue team with the help of firemen. As a survival, I didn't know whether it was my fortune or misfortune but when I recovered, the rescue team came to my home, one day. One of the members gave me a piece of paper in my hand in which the following text was written. lifeisnowhere. He asked me to read it carefully and I could hardly interpret the text as Life is now here, instead of Life is nowhere. All of them gave me a cute smile and went away and I decided to live peacefully and hopefully on their saying from then onwards and very soon. Because of this tragedy, I'm alone couldn't join a school but a curiosity to learn something made me a self-learner. I'm indeed a self-learner, so I'm likely not able to answer any questions on this site right now. In the field of computer science, my self-study mainly includes, QBASIC, C, C++, C#, VB, Java, JavaScript, PHP and a little about ASP.NET. Oracle, MySQL and MSSQL-Server with DBMS. and other theoretical subjects. I'm currently dealing with - Android and Java EE including Servlet, JSP-JSTL/EL (with Spring and Struts with ORM models JPA/Hibernate) and JSF.

Updated on June 09, 2022

Comments

  • Tiny
    Tiny almost 2 years

    Important Notice : This issue has been fixed as of PrimeFaces 5.2 final (Community Release) released on April 8, 2015. As such if you happened to use that version or newer, you would not need to fiddle around with a temporary workaround.

    The earlier given example can now safely be modified as follows.

    public StreamedContent getImage() throws IOException {
        FacesContext context = FacesContext.getCurrentInstance();
    
        if (context.getCurrentPhaseId() == PhaseId.RENDER_RESPONSE) {
            return new DefaultStreamedContent();
        } else {
            String id = context.getExternalContext().getRequestParameterMap().get("id");
            byte[] bytes = Utils.isNumber(id) ? service.findImageById(Long.parseLong(id)) : null;
            return bytes == null ? null : new DefaultStreamedContent(new ByteArrayInputStream(bytes));
        }
    }
    

    I have moved images to the database (MySQL) in the form of BLOB (LONGBLOB) after I got tired of manipulating/managing images stored in the disk file system.

    Accordingly, I'm displaying images in <p:dataTable> as follows (blatantly copied from here :) ).

    <p:column headerText="Header">
        <p:graphicImage value="#{bannerBean.image}" height="200" width="200">
            <f:param name="id" value="#{row.bannerId}"/>
        </p:graphicImage>
    <p:column>
    

    The bean that retrieves images is as follows.

    @ManagedBean
    @ApplicationScoped
    public final class BannerBean
    {
        @EJB
        private final BannerBeanLocal service=null;
        public BannerBean() {}
    
        public StreamedContent getImage() throws IOException {
            FacesContext context = FacesContext.getCurrentInstance();
    
            if (context.getCurrentPhaseId() == PhaseId.RENDER_RESPONSE) {
                return new DefaultStreamedContent();
            }
            else {
                String id = context.getExternalContext().getRequestParameterMap().get("id");
                byte[] bytes = service.findImageById(Long.parseLong(id));
                return bytes==null? new DefaultStreamedContent():new DefaultStreamedContent(new ByteArrayInputStream(bytes));
            }
        }
    }
    

    This works fine as long as there are images in each row of the underlying database table.

    The BLOB type column in the database is however, optional in some cases and hence, it can contain null values as well.

    If this column in any row/s in the database is null then, the following exception is thrown.

    SEVERE:   Error in streaming dynamic resource. null
    WARNING:   StandardWrapperValve[Faces Servlet]: Servlet.service() for servlet Faces Servlet threw exception
    java.lang.NullPointerException
        at org.primefaces.application.PrimeResourceHandler.handleResourceRequest(PrimeResourceHandler.java:127)
        at javax.faces.application.ResourceHandlerWrapper.handleResourceRequest(ResourceHandlerWrapper.java:153)
        at javax.faces.webapp.FacesServlet.service(FacesServlet.java:643)
        at org.apache.catalina.core.StandardWrapper.service(StandardWrapper.java:1682)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:344)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:214)
        at org.primefaces.webapp.filter.FileUploadFilter.doFilter(FileUploadFilter.java:70)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:256)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:214)
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:316)
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:160)
        at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:734)
        at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:673)
        at com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:99)
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:174)
        at org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:357)
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:260)
        at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:188)
        at org.glassfish.grizzly.http.server.HttpHandler.runService(HttpHandler.java:191)
        at org.glassfish.grizzly.http.server.HttpHandler.doHandle(HttpHandler.java:168)
        at org.glassfish.grizzly.http.server.HttpServerFilter.handleRead(HttpServerFilter.java:189)
        at org.glassfish.grizzly.filterchain.ExecutorResolver$9.execute(ExecutorResolver.java:119)
        at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeFilter(DefaultFilterChain.java:288)
        at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeChainPart(DefaultFilterChain.java:206)
        at org.glassfish.grizzly.filterchain.DefaultFilterChain.execute(DefaultFilterChain.java:136)
        at org.glassfish.grizzly.filterchain.DefaultFilterChain.process(DefaultFilterChain.java:114)
        at org.glassfish.grizzly.ProcessorExecutor.execute(ProcessorExecutor.java:77)
        at org.glassfish.grizzly.nio.transport.TCPNIOTransport.fireIOEvent(TCPNIOTransport.java:838)
        at org.glassfish.grizzly.strategies.AbstractIOStrategy.fireIOEvent(AbstractIOStrategy.java:113)
        at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.run0(WorkerThreadIOStrategy.java:115)
        at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.access$100(WorkerThreadIOStrategy.java:55)
        at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy$WorkerThreadRunnable.run(WorkerThreadIOStrategy.java:135)
        at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:564)
        at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.run(AbstractThreadPool.java:544)
        at java.lang.Thread.run(Thread.java:722)
    

    So how to manage null BLOBs so that this exception disappears?

    Returning new DefaultStreamedContent(new ByteArrayInputStream(new byte[0])), in case, the byte array in the managed bean is null would suppress the exception but this after all, should not be a solution. Is this a desired solution?


    The EJB method that returns a byte array though completely unnecessary, in this case.

    public byte[] findImageById(Long id)
    {
        CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
        CriteriaQuery<byte[]>criteriaQuery=criteriaBuilder.createQuery(byte[].class);
        Root<BannerImages> root = criteriaQuery.from(BannerImages.class);
        criteriaQuery.multiselect(root.get(BannerImages_.bannerImage));
        ParameterExpression<Long>parameterExpression=criteriaBuilder.parameter(Long.class);
        criteriaQuery.where(criteriaBuilder.equal(root.get(BannerImages_.bannerId), parameterExpression));
        List<byte[]> list = entityManager.createQuery(criteriaQuery).setParameter(parameterExpression, id).getResultList();
        return list!=null&&!list.isEmpty()?list.get(0):null;
    }
    
  • Tiny
    Tiny almost 10 years
    The issue has been reported.
  • Tiny
    Tiny almost 10 years
    Is it legal to decorate a Servlet by a JSF/CDI scope?
  • srbs
    srbs almost 10 years
    Good point, I seem to have failed to remove that line. I'll make that edit.
  • Chris
    Chris over 9 years
    The bug is listed as a new function to future release. How can that be, it had been working before. Almost half a year down the road and not fixed. To me this is rudimentary functionality that should be working?! Link: code.google.com/p/primefaces/issues/…
  • Tiny
    Tiny about 9 years
    The issue status on the tracker indicates "Fixed" for the incoming version PrimeFaces 5.2.