Search code examples
jsfthumbnailsomnifacesgraphicimage

How to create a thumbnail link to OmniFaces graphicImage method


I was wondering if and how it is possible to display an <o:graphicImage> as thumbnail inside a <p:lightbox>. To be more precise, I wanted to achieve something like this:

<p:dataTable id="articles" var="article"
  value="#{articleMB.articles}" selectionMode="single"
  rowKey="#{articles.id}"
  selection="#{articleMB.selectedArticle}">
  <p:column>
    <p:lightBox styleClass="imagebox" id="lighbox">  
      <h:outputLink value="#{imageBean.getFirstImage(article, true)}">  
        <o:graphicImage value="#{imageBean.getFirstImage(article, true)}" dataURI="true" height="80" />  
      </h:outputLink>
     </p:lightBox>
   </p:column>
</p:dataTable>

Which isn't working obviously, since there's no proper URL passed to the lightbox. imageBean.getFirstImage(Article article, boolean thumbnail) returns a byte[] of the image, since the images I want to access are stored on an external source.


Edit: So I've done as BalusC mentioned and it seems to be the proper approach. But now I'm getting the following exception:

Caused by: java.lang.IllegalArgumentException: java.lang.ClassCastException@2eae887c 
    at sun.reflect.GeneratedMethodAccessor307.invoke(Unknown Source) 
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 
    at java.lang.reflect.Method.invoke(Method.java:497) 
    at org.omnifaces.resourcehandler.GraphicResource.getInputStream(GraphicResource.java:259) 
    at com.sun.faces.application.resource.ResourceHandlerImpl.handleResourceRequest(ResourceHandlerImpl.java:335) 
    at javax.faces.application.ResourceHandlerWrapper.handleResourceRequest(ResourceHandlerWrapper.java:153) 
    at org.primefaces.application.resource.PrimeResourceHandler.handleResourceRequest(PrimeResourceHandler.java:87) 
    at javax.faces.application.ResourceHandlerWrapper.handleResourceRequest(ResourceHandlerWrapper.java:153) 
    at javax.faces.application.ResourceHandlerWrapper.handleResourceRequest(ResourceHandlerWrapper.java:153) 
    at javax.faces.webapp.FacesServlet.service(FacesServlet.java:655) 
    ... 32 more

This is the method that actually returns the image. It is working fine in every other context:

public byte[] getFirstImage(final Article article, boolean thumbnail)
{
    try
    {
        File dir = new File(getImageFolder(article.getImageFolder(), thumbnail));
        File[] files = dir.listFiles(new FilenameFilter()
        {
            @Override
            public boolean accept(File dir, String name)
            {
                return name.startsWith(String.valueOf(article.getArticleId()));
            }
        });
        Arrays.sort(files);
        return Files.readAllBytes(files[0].toPath());
    }
    catch (Exception e)
    {
        return new byte[1];
    }
}

Edit 2: As mentioned in the comments, I'm facing another weird behaviour. It runs perfectly fine on my local machine but on the server it throws following exception:

Caused by: java.lang.IllegalArgumentException: argument type mismatch
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.omnifaces.resourcehandler.GraphicResource.getInputStream(GraphicResource.java:259)
    at com.sun.faces.application.resource.ResourceHandlerImpl.handleResourceRequest(ResourceHandlerImpl.java:335)
    at javax.faces.application.ResourceHandlerWrapper.handleResourceRequest(ResourceHandlerWrapper.java:153)
    at org.primefaces.application.resource.PrimeResourceHandler.handleResourceRequest(PrimeResourceHandler.java:87)
    at javax.faces.application.ResourceHandlerWrapper.handleResourceRequest(ResourceHandlerWrapper.java:153)
    at javax.faces.application.ResourceHandlerWrapper.handleResourceRequest(ResourceHandlerWrapper.java:153)
    at javax.faces.webapp.FacesServlet.service(FacesServlet.java:655)
    ... 32 more

Solution

  • For exactly this reason, OmniFaces 2.5 introduced the #{of:graphicImageURL()} EL function. This works only if your ImageBean is annotated with @GraphicImageBean.

    import org.omnifaces.cdi.GraphicImageBean;
    
    @GraphicImageBean
    public class ImageBean {
    
        // ...
    
    }
    

    <h:outputLink value="#{of:graphicImageURL('imageBean.getFirstImage(article, false)')}">  
        <o:graphicImage value="#{imageBean.getFirstImage(article, true)}" dataURI="true" height="80" />  
    </h:outputLink>
    

    See also the @GraphicImageBean showcase.


    Update: to be clear, you need a converter for non-standard types such as Article. Why such a converter is needed and how to create it is detailed in Conversion Error setting value for 'null Converter'. If you create a @FacesConverter(forClass=Article.class), then the OmniFaces GraphicImage will automatically pickup it.

    In your particular case it's however more efficient to just pass the identifier to the image streamer method right away, this saves an additional conversion step. Provided that your Article object has an id property, here's how:

    <h:outputLink value="#{of:graphicImageURL('imageBean.getFirstImage(article.id, false)')}">  
        <o:graphicImage value="#{imageBean.getFirstImage(article.id, true)}" dataURI="true" height="80" />  
    </h:outputLink>
    

    public byte[] getFirstImage(Long articleId, boolean thumbnail) {
        // ...
    }
    

    Namely, JSF already has builtin converters for standard types such as Long and boolean.