2

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
Jonas Bausch
  • 185
  • 3
  • 12

1 Answers1

2

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.

Community
  • 1
  • 1
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • thanks, I'm pretty sure this is the right way to do it but it keeps telling me 'javax.el.ELException: Function `of:graphicImageURL' not found`. I'm sure I included omnifaces the right way since wildfly tells me on server startup `08:42:29,600 INFO [org.omnifaces.ApplicationInitializer] (ServerService Thread Pool -- 68) Using OmniFaces version 2.5.1` – Jonas Bausch Sep 28 '16 at 12:47
  • That can happen if XML namespace declaration `xmlns:of="http://omnifaces.org/functions"` is missing in the template. – BalusC Sep 28 '16 at 12:53
  • thanks, that solved at least that problem. Now, the thumbnail is displayed as usual but the lightbox still doesn't show any image (the spinner keeps spinning forever). I also get an exception saying: `2016-09-28 15:04:52,091 ERROR [io.undertow.request] (default task-21) UT005023: Exception handling request to /ToyJoTCS/javax.faces.resource/ImageBean_getFirstImage.xhtml: javax.servlet.ServletException: java.lang.ClassCastException@4cdaa9e4 ... Caused by: java.lang.IllegalArgumentException: java.lang.ClassCastException@4cdaa9e4` – Jonas Bausch Sep 28 '16 at 13:16
  • As usual, the answer is in the stack trace. – BalusC Sep 28 '16 at 13:46
  • What I can extract from that stacktrace is that something wants to be casted into something it can't be casted. What's also odd to me is the `request to /ToyJoTCS/javax.faces.resource/ImageBean_getFirstImage.xhtml‌​: `. Sorry for being stumped on this one, but I don't get it. I thought, of:graphicImageURL() would accept a method that returns a byte[]. – Jonas Bausch Sep 28 '16 at 14:12
  • If you don't post the stack trace, I can't translate it for you in layman's terms. – BalusC Sep 28 '16 at 14:13
  • As per the stack trace, it appears that the method is invoked with a wrongly typed argument. Do you have a `@FacesConverter(forClass=Article.class)` as instructed in `` showcase? http://showcase.omnifaces.org/components/graphicImage – BalusC Oct 05 '16 at 08:59
  • No I haven't. Can you give me a little direction on that? I've implemented an ArticleConverter, currently with just the standard method stub returning the value they retrieve. `getAsString()` returns something like `/ToyJoTCS/javax.faces.resource/ImageBean_getFirstImage.jpg.xhtml?ln=omnifaces.graphic&v=0&p=de.jb.toyjo.model.Article%4079eb5990&p=true` Don't know exactly what to do with that. – Jonas Bausch Oct 05 '16 at 12:44
  • How to properly create JSF converters is detailed in this Q&A: http://stackoverflow.com/q/4734580 The `getAsString()` should return some identifier of the `Article` object and the `getAsObject()` should return the very same `Article` object based on exactly that identifier. – BalusC Oct 05 '16 at 13:43
  • Alternatively, you can also just pass `#{article.id}` orso to the bean method and then have the method use it further. That's more efficient without the need for double conversion. See updated answer. – BalusC Oct 05 '16 at 13:47
  • That's exactly what I did and works like a charm. But just out of curiousity: How could I convert `/ToyJoTCS/javax.faces.resource/ImageBean_getFirstImage.jpg.x‌​html?ln=omnifaces.gr‌​aphic&v=0&p=de.jb.to‌​yjo.model.Article%40‌​79eb5990&p=true` into something useful? Should I try to parse that String, take the `40‌​79eb5990` part and try to create that into an object? – Jonas Bausch Oct 05 '16 at 14:14
  • Your converter needs to return `article.getId()` in `getAsString()`. Then the `p` parameter will get exactly that value, e.g. if it's `42` like so `...&p=42&p=true` This value will then be passed to `getAsObject()` of the converter, which should then return the desired `Article` object. Again, see the aforementioned link as to how to create a proper JSF converter. – BalusC Oct 05 '16 at 14:17
  • OK, thanks a lot. By the way: it only works when I use a `Boolean` argument instead of the primitive type `boolean`. – Jonas Bausch Oct 05 '16 at 14:31
  • Ah, this is unexpected. Which WildFly version are you using? And which Mojarra version if you modified WildFly default one? – BalusC Oct 05 '16 at 15:11
  • I'm **using Mojarra 2.2.12-jbossorg-2** and **WildFly Full 9.0.2.Final (WildFly Core 1.0.2.Final)** – Jonas Bausch Oct 05 '16 at 19:10
  • I discovered another strange behaviour: I have two instances of my app running on two different servers, both with the same setup. It's working perfectly on server A but server B throws an `IllegalArgumentException: argument type mismatch` when trying to show the image in the lightbox (thumbnail is displayed fine). They both share the exact same code and even on my local machine it's perfectly working. If that's too off-topic I can create a new question. Any ideas? – Jonas Bausch Oct 06 '16 at 17:57
  • If you can't read stack traces, you need to post it, otherwise I can't translate it for you in layman's terms. – BalusC Oct 06 '16 at 18:10
  • This is indeed strange. Apparently something's wrong with that boolean/Boolean. Which appserver impl/version and jdk version exactly is that server using? Are you sure the webapp itself is free of any server-specific libraries in its `/WEB-INF/lib`? Then I can try to reproduce. A theoretical work around would be creating two separate methods, a `getThumb(article.id)` and `getFull(article.id)`. – BalusC Oct 07 '16 at 07:11
  • I don't know why and how, but changing the method to `imageBean.getImage(Long articleId, int index, Boolean thumbnail)` solved it. Anyhow, I created a new [question](http://stackoverflow.com/questions/39918403/omnifaces-graphicimage-method-throws-illegalargumentexception) since this is very odd behaviour to me – Jonas Bausch Oct 07 '16 at 13:29