0

com.I have SWF app where user uploads image, and that image has to be displayed in the next flow state. The flow uses conversation-scoped bean (actually a JPA entity) that stores all user-entered data. I can upload the image, store it in the bean, but PrimeFaces graphicImage facelet will not render it. I did some research, and it appears that during rendering PF GI component stores the EL expression to be evaluated with the session, and subsequent call to ResourceHandler (made by IMG tag in rendered HTML) fetches the same EL expression and actually evaluates it. Problem is that ResourceHandler has no access to the flow variable where I store all my data and therefore cant render the image.

I extended PF GI component to store actual image with the session instead of EL expression, but that has obvious performance implications: storing binary data in Session is not a good idea. Also, I would rather keep all data in flow/conversation scope instead of session for security reasons.

Is there any clean way to make PF GI component access my flow variables?

Snippets of my code below

The Flow:

<on-start>
    <evaluate expression="new my.models.Customers()" result="conversationScope.customer"></evaluate>
</on-start>

<view-state id="scanImage" view="s1-scan.xhtml" model="customer" >
    <secured attributes="ROLE_WEB" />
    <transition on="submit" to="verifyImage"></transition>
    <transition on="cancel" to="canceled"></transition>
</view-state>

<view-state id="verifyImage" view="s2-verifyscan.xhtml" model="customer">
    <secured attributes="ROLE_WEB" />
    <transition on="submit" to="enterDetails"></transition>
    <transition on="back" to="scanImage"></transition>
    <transition on="cancel" to="canceled"></transition>
</view-state>

Image rendering:

<p:graphicImage value="#{customer.getImageSC()}"></p:graphicImage>

Patched GraphicImageRenderer.getImageSrc()

protected String getImageSrc(FacesContext context, GraphicImage image) {
    String src = null;
String name = image.getName();

if(name != null) {
    String libName = image.getLibrary();
    ResourceHandler handler = context.getApplication().getResourceHandler();
    Resource res = handler.createResource(name, libName);

    if(res == null) {
        return "RES_NOT_FOUND";
    } 
    else {
        String requestPath = res.getRequestPath();

        return context.getExternalContext().encodeResourceURL(requestPath);
    }
}
else {
    Object value = image.getValue();

    if(value == null) {
        return "";
    }
    else  if(value instanceof String) {
        src = getResourceURL(context, (String) value);
    }
    else if(value instanceof StreamedContent) {
        StreamedContent streamedContent = (StreamedContent) value;
        Resource resource = context.getApplication().getResourceHandler().createResource("dynamiccontent", "primefaces", streamedContent.getContentType());
        String resourcePath = resource.getRequestPath();
        String rid = createUniqueContentId(context);
        StringBuilder builder = new StringBuilder(resourcePath);

        builder.append("&").append(Constants.DYNAMIC_CONTENT_PARAM).append("=").append(rid);

        for(UIComponent kid : image.getChildren()) {
            if(kid instanceof UIParameter) {
                UIParameter param = (UIParameter) kid;

                builder.append("&").append(param.getName()).append("=").append(param.getValue());
            }
        }

        src = builder.toString();

        context.getExternalContext().getSessionMap().put(rid, value);
        // context.getExternalContext().getSessionMap().put(rid, image.getValueExpression("value").getExpressionString());
    }

    if(!image.isCache()) {
        src += src.contains("?") ? "&" : "?";
        src += "primefaces_image=" + UUID.randomUUID().toString();
    }

    src = context.getExternalContext().encodeResourceURL(src);
}

    return src;
}

Handler:

public void handleResourceRequest(FacesContext context) throws IOException {
Map<String,String> params = context.getExternalContext().getRequestParameterMap();
String library = params.get("ln");
String dynamicContentId = params.get(Constants.DYNAMIC_CONTENT_PARAM);

if(dynamicContentId != null && library != null && library.equals("primefaces")) {
    Map<String,Object> session = context.getExternalContext().getSessionMap();
    StreamedContent streamedContent = null;

    try {

        Object dynamicContentEL = (Object) session.get(dynamicContentId);

        //String dynamicContentEL = (String) session.get(dynamicContentId);
        //ELContext eLContext = context.getELContext();
        //ValueExpression ve = context.getApplication().getExpressionFactory().createValueExpression(context.getELContext(), dynamicContentEL, StreamedContent.class);
        //streamedContent = (StreamedContent) ve.getValue(eLContext);

        streamedContent = (StreamedContent) dynamicContentEL;

        ExternalContext externalContext = context.getExternalContext();
        externalContext.setResponseStatus(200);
        externalContext.setResponseContentType(streamedContent.getContentType());

        byte[] buffer = new byte[2048];

        int length;
        InputStream inputStream = streamedContent.getStream();
        while ((length = (inputStream.read(buffer))) >= 0) {
            externalContext.getResponseOutputStream().write(buffer, 0, length);
        }

        externalContext.responseFlushBuffer();
        context.responseComplete();

    } catch(Exception e) {
        logger.log(Level.SEVERE, "Error in streaming dynamic resource. {0}", new Object[]{e.getMessage()});
    }
    finally {
        //cleanup
        session.remove(dynamicContentId);

        if(streamedContent != null) {
            streamedContent.getStream().close();
        }
    }
}
else {
   super.handleResourceRequest(context); 
}

}

faces-config:

   <application>
        <el-resolver>org.springframework.web.jsf.el.SpringBeanFacesELResolver</el-resolver>
        <resource-handler>com.my.PrimeResourceHandler</resource-handler>
    </application>

<render-kit>    
    <renderer>
         <component-family>org.primefaces.component</component-family>
         <renderer-type>org.primefaces.component.graphicimage.GraphicImageRenderer</renderer-type>
         <renderer-class>com.my.GraphicImageRenderer</renderer-class>
    </renderer>
</render-kit>

I suspect it may be possible to store webflow context together with the EL in Session object, and make resource handler to use that context when evaluating the EL...

rootkit
  • 2,165
  • 2
  • 29
  • 43

1 Answers1

0

Did you modify the primefaces code(i.e. GraphicImageRenderer and PrimeResourceHandler classes) or extend them? If you extended them how did the code know to use/call the extended classes?

I am using a request scope and storing all images in the WEB-INF folder and retrieving them using below:

<p:graphicImage value="/images/{springBean.imageFileName}"> 

I did not make any changes to primefaces code as they may be a fix for this in later versions. I like to use flow scope though and also would like to retrieve them using the bean like below:

<p:graphicImage value="#{springBean.imageFile}">

I also tried saving the images to hard-drive location not in webapproot and cannot show the graphicImage. wish I can map all requests to /images/* to some local folder without using controller/managed bean like below in websphere?

How I save and retrieve an image on my server in a java webapp

Community
  • 1
  • 1
Julie
  • 179
  • 2
  • 13
  • No I didnt modify PF code. Edited the question to include relevant portions of faces-config.xml – rootkit Nov 16 '12 at 23:03