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;
}