Tag Archive | session

Passing SFSBs as an argument to a custom JSF component?

Have you ever developed a custom JSF component that renders an img tag? If so, you will know that this component consists at least of two parts: The first part is the implementation of UIComponent that actually renders the img tag, the second part is the servlet filter or phase listener that answers the asynchronously incoming request for the image URL. Does it work, to use a stateful session bean (SFSB) to load the image data from the database?
First of all we start with the UIComponent. As base class we choose UIOutput, which is sufficient for the current case:

@FacesComponent("EjbComponent")
public class EjbComponent extends UIOutput {
    private static final Logger logger = LoggerFactory.getLogger(EjbComponent.class);
    private static final String COMPONENT_FAMILY = "martins.developer.world.jsf.component.helloWorld";

    private enum PropertyKeys {
        statefulEjb
    };

    @Override
    public String getFamily() {
        return COMPONENT_FAMILY;
    }
    ...
}

We override the encodeBegin() method to render the img tag. We get the stateful session bean, which is passed as an argument to the component, using JSF’s StateHelper:

    @Override
    public void encodeBegin(FacesContext facesContext) throws IOException {
        ResponseWriter writer = facesContext.getResponseWriter();
        String imageSrc = createImageUrl(facesContext);
        writer.startElement("img", this);
        writer.writeAttribute("src", imageSrc, "");
        writer.endElement("img");
        Map<String,Object> sessionMap = FacesContext.getCurrentInstance().getExternalContext().getSessionMap();
        sessionMap.put("ejbComponent", getStatefulEjb());
    }

    public StatefulEjbLocal getStatefulEjb() {
        return (StatefulEjbLocal) getStateHelper().eval(PropertyKeys.statefulEjb);
    }

    public void setStatefulEjb(StatefulEjbLocal statefulEjb) {
        getStateHelper().put(PropertyKeys.statefulEjb, statefulEjb);
    }

The createImageUrl() method adds a parameter to the HTTP request, such that we can detect this specific request later on in the PhaseListener:

    private String createImageUrl(FacesContext context) {
        StringBuilder builder = new StringBuilder(context.getExternalContext().getRequestContextPath());
        if (builder.indexOf("?") == -1) {
            builder.append('?');
        } else {
            builder.append('&');
        }
        builder.append("ejbComponent").append("=").append("ejbComponent");
        return builder.toString();
    }

The PhaseListener has the task to detect the incoming request issued by the img tag rendered by the component above:

public class EjbComponentPhaseListener implements PhaseListener {
    private static final Logger logger = LoggerFactory.getLogger(EjbComponentPhaseListener.class);

    @Override
    public void beforePhase(PhaseEvent phaseEvent) {
        FacesContext facesContext = phaseEvent.getFacesContext();
        ExternalContext externalContext = facesContext.getExternalContext();
        String ejbComponentParameter = requestParameter.get("ejbComponent");
        if (ejbComponentParameter != null) {
            Map<String, Object> sessionMap = externalContext.getSessionMap();
            StatefulEjbLocal ejbComponent = (StatefulEjbLocal) sessionMap.get("ejbComponent");
            if (ejbComponent != null) {
                byte[] image = ejbComponent.getImage();
                HttpServletResponse response = (HttpServletResponse) externalContext.getResponse();
                ServletOutputStream outputStream = null;
                try {
                    outputStream = response.getOutputStream();
                    IOUtils.copy(new ByteArrayInputStream(image), outputStream);
                    outputStream.flush();
                } catch (Exception e) {
                    logger.error(e.getMessage(), e);
                } finally {
                    IOUtils.closeQuietly(outputStream);
                    facesContext.responseComplete();
                }
            } else {
                logger.debug("Could not retrieve ejbComponent from session.");
            }
        } else {
            logger.debug("Request parameter not found");
        }
    }

    @Override
    public void afterPhase(PhaseEvent phaseEvent) {
        logger.debug("afterPhase()");
    }

    @Override
    public PhaseId getPhaseId() {
        return PhaseId.RENDER_RESPONSE;
    }

As you see, we try to see if the currently handled request has a parameter with the name “ejbComponent”. If this is the case, we look up the SFSB from the session map, as we have put it in there before (see code for the JSF component). Now that we have a reference to the SFSB, we can use it to load the image data and pass it to the browser.
In our web application we now create a simple backing bean that holds a reference to the SFSB, which later on is passed to the component on the JSF page:

@Named
public class HelloWorldBackingBean {
    @EJB
    private StatefulEjbLocal statefulEjb;
    ...

Finally we pass the SFSB from the backing bean to the component on our JSF page:

<mdw:ejbComponent statefulEjb="#{helloWorldBackingBean.statefulEjb}"/>

If we compile and deploy the application, the JSF will render the image as expected. But there is one caveat: If the same SFSB is used to render more than one image within the same web session, the concurrent access to the SFSB is serialized as the EJB 3.1 specification demands this in section 4.3.14. This means that each invocation locks/blocks the other threads until a timeout occurs. This timeout can be given with the @AccessTimeout annotation, the default in the JBoss AS 7.x container is 5 seconds. If the waiting threads therefore have to wait too long, a ConcurrentAccessException is thrown. This may lead to sporadic failures that only happen, if the system is under load.
Sample code can be found in my github repository.