Search code examples
jsfjsf-2.2omnifacesgraphicimage

o:graphicImage throws java.lang.IllegalArgumentException: argument type mismatch


I can´t display images from my database, they are stored as bytea and I am mapping them like this:

@Entity
@Table(name = "photograph", schema = "public")
public class Photograph{
    private PhotographId id;

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "diagnostic_id", nullable = false, insertable = false, updatable = false)
    private Diagnostic diagnostic;

    @Column(name = "photograph_content_type")
    private String photographContentType;

    @EmbeddedId
    @AttributeOverrides({
        @AttributeOverride(name = "diagnosticId", column = @Column(name  = "diagnostic_id", nullable = false)),
        @AttributeOverride(name = "photo", column = @Column(name = "photo", nullable = false)),
        @AttributeOverride(name = "photographDescription", column = @Column(name = "photograph_description", nullable = false, length = 100)) })
    public PhotographId getId() {
       return this.id;
    }

//Getters and Setters...
}

This is the embedable class for the pk:

@Embeddable
public class PhotographId{
    @Column(name = "diagnostic_id", nullable = false)
    private Long diagnosticId;
    @Column(name = "photo", nullable = false)
    private byte[] photo;
    @Column(name = "photograph_description", nullable = false, length = 100)
    private String photographDescription;

//Getters and Setters...
}

I can store all the images in the database with no problem. The problem is when I want to show them in a p:dataTable like this:

<p:dataTable id="dataTableLoadedPhotos"
                value="#{imageController.photographListUpdate}" var="image">
                <p:column headerText="Fotografías cargadas" width="110">
                    <o:graphicImage value="#{imageStreamer.getById(image.id)}"
                        alt="#{msgs['label.diagnostic.photograph.notFound']}" />
                </p:column>
            </p:dataTable>

I am using a streamer based on The BalusC Code: ImageServlet and I tried to use o:graphicImage with no success, something is missing in mi code:

@ManagedBean
@ApplicationScoped
public class ImageStreamer {

@EJB
private PhotographService photographService;

public byte[] getById(PhotographId id) {
    try {
        return photographService.getContent(id);
    } catch (ServiceException e) {
        FacesMessage mensaje = new FacesMessage(
                FacesMessage.SEVERITY_ERROR,
                "Error al buscar la fotografía "
                        + id.getPhotographDescription(), e.getMessage());
        FacesContext.getCurrentInstance().addMessage(null, mensaje);
    }
    return null;
}
}

I also have a managed bean with @RequestScoped:

@ManagedBean
@RequestScoped
public class ImageController {

@EJB
private PhotographService photographService;

@ManagedProperty(value = "#{diagnosticDataManager}")
private DiagnosticDataManager diagnosticDataManager;

private List<Photograph> photographListUpdate = new ArrayList<Photograph>();
private Photograph selectedPhoto;

/**
 * 
 */
public ImageController() {
    diagnosticDataManager = new DiagnosticDataManager();
}

@PostConstruct
public void init() {
    if (diagnosticDataManager.getDiagnostic().getDiagnosticId() != null)
        photographListUpdate = photographService
                .findPhotosByDiagnostic(diagnosticDataManager
                        .getDiagnostic());

    for (Photograph photograph : photographListUpdate) {
        byte[] imageContent = org.apache.commons.codec.binary.Base64
                .decodeBase64(photograph.getId().getPhoto());
        ExternalContext ec = FacesContext.getCurrentInstance()
                .getExternalContext();
        ec.getSessionMap()
                .put(photograph.getId().toString(),
                        imageContent);
    }
}
// Getters and setters....
}

As I am using maven, I have my app directory like this:

src
|----main
     |----webapp
          |----images

I have an error in my server.log, and I can't see the image in the page, what is missing in my code?

20:22:43,687 ERROR [org.apache.catalina.core.ContainerBase.[jboss.web].[default-host].[/patientdiagnostics].[Faces Servlet]] (http-localhost/127.0.0.1:8080-1) JBWEB000236: Servlet.service() for servlet Faces Servlet threw exception: java.lang.IllegalArgumentException: argument type mismatch
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) [rt.jar:1.7.0_79]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) [rt.jar:1.7.0_79]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) [rt.jar:1.7.0_79]
at java.lang.reflect.Method.invoke(Method.java:606) [rt.jar:1.7.0_79]
at org.omnifaces.resourcehandler.GraphicResource.getInputStream(GraphicResource.java:241) [omnifaces-2.0.jar:2.0]
at com.sun.faces.application.resource.ResourceHandlerImpl.handleResourceRequest(ResourceHandlerImpl.java:260) [jsf-impl-2.1.28.redhat-3.jar:2.1.28.redhat-3]
at javax.faces.application.ResourceHandlerWrapper.handleResourceRequest(ResourceHandlerWrapper.java:125) [jboss-jsf-api_2.1_spec-2.1.28.Final-redhat-1.jar:2.1.28.Final-redhat-1]
at org.primefaces.application.resource.PrimeResourceHandler.handleResourceRequest(PrimeResourceHandler.java:74) [primefaces-5.1.jar:5.1]
at javax.faces.application.ResourceHandlerWrapper.handleResourceRequest(ResourceHandlerWrapper.java:125) [jboss-jsf-api_2.1_spec-2.1.28.Final-redhat-1.jar:2.1.28.Final-redhat-1]
at javax.faces.application.ResourceHandlerWrapper.handleResourceRequest(ResourceHandlerWrapper.java:125) [jboss-jsf-api_2.1_spec-2.1.28.Final-redhat-1.jar:2.1.28.Final-redhat-1]
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:591) [jboss-jsf-api_2.1_spec-2.1.28.Final-redhat-1.jar:2.1.28.Final-redhat-1]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:295) [jbossweb-7.4.8.Final-redhat-4.jar:7.4.8.Final-redhat-4]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:214) [jbossweb-7.4.8.Final-redhat-4.jar:7.4.8.Final-redhat-4]
at org.primefaces.webapp.filter.FileUploadFilter.doFilter(FileUploadFilter.java:105) [primefaces-5.1.jar:5.1]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:246) [jbossweb-7.4.8.Final-redhat-4.jar:7.4.8.Final-redhat-4]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:214) [jbossweb-7.4.8.Final-redhat-4.jar:7.4.8.Final-redhat-4]
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:231) [jbossweb-7.4.8.Final-redhat-4.jar:7.4.8.Final-redhat-4]
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:149) [jbossweb-7.4.8.Final-redhat-4.jar:7.4.8.Final-redhat-4]
at org.jboss.as.jpa.interceptor.WebNonTxEmCloserValve.invoke(WebNonTxEmCloserValve.java:50) [jboss-as-jpa-7.4.0.Final-redhat-19.jar:7.4.0.Final-redhat-19]
at org.jboss.as.jpa.interceptor.WebNonTxEmCloserValve.invoke(WebNonTxEmCloserValve.java:50) [jboss-as-jpa-7.4.0.Final-redhat-19.jar:7.4.0.Final-redhat-19]
at org.jboss.as.web.security.SecurityContextAssociationValve.invoke(SecurityContextAssociationValve.java:169) [jboss-as-web-7.4.0.Final-redhat-19.jar:7.4.0.Final-redhat-19]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:145) [jbossweb-7.4.8.Final-redhat-4.jar:7.4.8.Final-redhat-4]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:97) [jbossweb-7.4.8.Final-redhat-4.jar:7.4.8.Final-redhat-4]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:102) [jbossweb-7.4.8.Final-redhat-4.jar:7.4.8.Final-redhat-4]
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:344) [jbossweb-7.4.8.Final-redhat-4.jar:7.4.8.Final-redhat-4]
at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:856) [jbossweb-7.4.8.Final-redhat-4.jar:7.4.8.Final-redhat-4]
at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:653) [jbossweb-7.4.8.Final-redhat-4.jar:7.4.8.Final-redhat-4]
at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:926) [jbossweb-7.4.8.Final-redhat-4.jar:7.4.8.Final-redhat-4]
at java.lang.Thread.run(Thread.java:745) [rt.jar:1.7.0_79]

I also tried something similar to Display database blob images in <p:graphicImage> inside <ui:repeat>


Solution

  • As stated in its documentation and showcase,

    In case the property is a method expression taking arguments, each of those arguments will be converted to a string HTTP request parameter and back to actual objects using the converters registered by class as available via Application.createConverter(Class). So, most of standard types like Long are already implicitly supported. In case you need to supply a custom object as argument for some reason, you need to explicitly register a converter for it yourself via @FacesConverter(forClass).

    the <o:graphicImage> will convert all EL method arguments to string and back using the JSF converters registered for the class of the argument type. JSF has already builtin converters for e.g. Long type, so that's why they work out the box. But you've there a PhotographId type as EL method argument and apparently no JSF converter registered for that.

    You've 2 options:

    1. Don't supply a whole entity as argument. Use its identifier instead, which is in your case just Long.

      E.g.

      <o:graphicImage value="#{imageStreamer.getById(image.id.diagnosticId)}">
      

      And alter the getById() method accordingly.

    2. Create a JSF converter for the PhotographId class.

      @FacesConverter(forClass=PhotographId.class)
      public class PhotographIdConverter implements Converter {
      
          @Override
          public String getAsString(FacesContext context, UIComponent component, Object modelValue) {
              // Write code here which converts PhotographId to its unique String representation.
          }
      
          @Override
          public Object getAsObject(FacesContext context, UIComponent component, String submittedValue) {
              // Write code here which coverts the PhotographId's unique String representation as created in above method back to the original PhotographId object.
          }
      
      }
      

    That said, the model looks strange. The photograph's content is part of the composite key..?? This is very very inefficient in both SQL and Java sides. You might as well have it reviewed and fixed (and then you'd need to alter the <o:graphicImage> usage accordingly).

    Also, that faces message which you're trying to add in the catch block of the streamer won't show up anywhere as the image request doesn't represent a JSF page. You'd better just log/mail it.