JSF 2 Captcha using <h:graphicImage rendering twice for Servlet generated image value working only for Chrome - jsf-2

I have an issue in my application using where I have a Captcha component built as a JSF Custom Tag:
in my JavaEE 6 webapp I use:
JSF 2.1 + Jboss Richfaces 4.2.3 + EJB 3.1 + JPA 2.0 + PrettyFaces 3.3.3
I have a JSF2 custom tag that is:
<tag>
<tag-name>captcha</tag-name>
<source>tags/captcha.xhtml</source>
</tag>
in my XHTML page called accountEdit.xhtml I have the captcha being displayed:
<ui:fragment rendered="#{customerMB.screenComponent.pageName eq 'create'}">
<div class="form_row">
<label class="contact"><strong>#{msg.captcha}:</strong>
</label>
<atl:captcha></atl:captcha>
</div>
</ui:fragment>
in captcha.xhtml:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:a4j="http://richfaces.org/a4j"
xmlns:rich="http://richfaces.org/rich">
<table border="0">
<tr>
<td>
<h:graphicImage id="capImg" value="#{facesContext.externalContext.requestContextPath}/../captcha.jpg" />
</td>
<td><a4j:commandButton id="resetCaptcha" value="#{msg.changeImage}" immediate="true" action="#{userMB.resetCaptcha}" >
<a4j:ajax render="capImg" execute="#this" />
</a4j:commandButton></td>
</tr>
<tr>
<td><h:inputText value="#{userMB.captchaComponent.captchaInputText}" /></td>
</tr>
</table>
</ui:composition>
in my web.xml I have configured a CaptchaServlet that handles the request for generating a captcha during runtime:
<servlet>
<servlet-name>CaptchaServlet</servlet-name>
<servlet-class>com.myapp.web.common.servlet.CaptchaServlet</servlet-class>
<init-param>
<description>passing height</description>
<param-name>height</param-name>
<param-value>30</param-value>
</init-param>
<init-param>
<description>passing width</description>
<param-name>width</param-name>
<param-value>120</param-value>
</init-param>
<load-on-startup>3</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>CaptchaServlet</servlet-name>
<url-pattern>/captcha.jpg</url-pattern>
</servlet-mapping>
My CaptchaServlet implementation:
public class CaptchaServlet extends HttpServlet {
/**
*
*/
private static final long serialVersionUID = 6105436133454099605L;
private int height = 0;
private int width = 0;
public static final String CAPTCHA_KEY = "captcha_key_name";
#Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
height = Integer
.parseInt(getServletConfig().getInitParameter("height"));
width = Integer.parseInt(getServletConfig().getInitParameter("width"));
}
#Override
protected void doGet(HttpServletRequest req, HttpServletResponse response)
throws IOException, ServletException {
// Expire response
response.setHeader("Cache-Control", "no-cache");
response.setDateHeader("Expires", 0);
response.setHeader("Pragma", "no-cache");
response.setDateHeader("Max-Age", 0);
BufferedImage image = new BufferedImage(width, height,
BufferedImage.TYPE_INT_RGB);
Graphics2D graphics2D = image.createGraphics();
Hashtable<TextAttribute, Object> map = new Hashtable<TextAttribute, Object>();
Random r = new Random();
String token = Long.toString(Math.abs(r.nextLong()), 36);
String ch = token.substring(0, 6);
Color c = new Color(0.6662f, 0.4569f, 0.3232f);
GradientPaint gp = new GradientPaint(30, 30, c, 15, 25, Color.white,
true);
graphics2D.setPaint(gp);
Font font = new Font("Verdana", Font.CENTER_BASELINE, 26);
graphics2D.setFont(font);
graphics2D.drawString(ch, 2, 20);
graphics2D.dispose();
HttpSession session = req.getSession(true);
session.setAttribute(CAPTCHA_KEY, ch);
OutputStream outputStream = response.getOutputStream();
ImageIO.write(image, "jpeg", outputStream);
outputStream.close();
}
}
When I run this app on Glassfish 3.1.1
when the Servlet's doGet() method is called while rendering
for the HttpServlet doGet() method that renders:
<h:graphicImage id="capImg" value="#{facesContext.externalContext.requestContextPath}/../captcha.jpg" />
doGet() renders only once for Google Chrome, thus rendering correctly.
For Firefox and IE doGet() renders twice updating the Captcha Key but not updating the painted Captcha Image on the page.
If anyone might know what could be a fix for this and why it has this behavior for Chrome different from other browsers please let me.
Thanks in advance!

The browser is caching the response. Your attempt to avoid this is incomplete and incorrect:
response.setHeader("Cache-Control", "no-cache");
response.setDateHeader("Expires", 0);
response.setHeader("Pragma", "no-cache");
response.setDateHeader("Max-Age", 0);
Please refer How to control web page caching, across all browsers? for the proper set:
response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); // HTTP 1.1.
response.setHeader("Pragma", "no-cache"); // HTTP 1.0.
response.setDateHeader("Expires", 0); // Proxies.
Further, to make it more robust, add a query string with the current timestamp in millis to the image URL. Here's an example provided that you've a java.util.Date instance as managed bean with the name now:
<h:graphicImage id="capImg" value="#{request.contextPath}/../captcha.jpg?#{now.time}" />
(please note that I also simplified the way to get the request context path, I only don't understand how it's useful if you go to domain root by ../ anyway)

I found a solution for this, is not the optimal solution but it works, here it goes:
captcha.xhtml
<table border="0">
<tr>
<td>
<h:graphicImage url="#{request.contextPath}/../jcaptcha"/>
</td>
<td>
<input type='text' name='j_captcha_response' value='' />
</td>
</tr>
</table>
CaptchaServlet doGet method:
protected void doGet(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws ServletException, IOException {
byte[] captchaChallengeAsJpeg = null;
// the output stream to render the captcha image as jpeg into
ByteArrayOutputStream jpegOutputStream = new ByteArrayOutputStream();
try {
// get the session id that will identify the generated captcha.
//the same id must be used to validate the response, the session id is a good candidate!
String captchaId = httpServletRequest.getSession().getId();
// call the ImageCaptchaService getChallenge method
BufferedImage challenge =
CaptchaServiceSingleton.getImageChallengeForID(captchaId,
httpServletRequest.getLocale());
// a jpeg encoder
JPEGImageEncoder jpegEncoder =
JPEGCodec.createJPEGEncoder(jpegOutputStream);
jpegEncoder.encode(challenge);
} catch (IllegalArgumentException e) {
httpServletResponse.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
} catch (CaptchaServiceException e) {
httpServletResponse.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
return;
}
captchaChallengeAsJpeg = jpegOutputStream.toByteArray();
// flush it in the response
httpServletResponse.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); // HTTP 1.1.
httpServletResponse.setHeader("Pragma", "no-cache");
httpServletResponse.setDateHeader("Expires", 0);
httpServletResponse.setContentType("image/jpeg");
ServletOutputStream responseOutputStream =
httpServletResponse.getOutputStream();
responseOutputStream.write(captchaChallengeAsJpeg);
responseOutputStream.flush();
responseOutputStream.close();
}
created CaptchaServiceRequestSingleton.java
package com.myapp.web.common.listener;
import java.awt.image.BufferedImage;
import java.util.HashMap;
import java.util.Locale;
import com.octo.captcha.service.image.DefaultManageableImageCaptchaService;
import com.octo.captcha.service.image.ImageCaptchaService;
public class CaptchaServiceSingleton {
private static ImageCaptchaService instance = new DefaultManageableImageCaptchaService();
private static final int MAX_CACHE_SIZE = 200;
private static HashMap<String, BufferedImage> captchaImgCache = new HashMap<String, BufferedImage>();
public static ImageCaptchaService getInstance(){
return instance;
}
public static BufferedImage getImageChallengeForID(String id, Locale locale) {
if (captchaImgCache.containsKey(id)) {
return captchaImgCache.get(id);
} else {
BufferedImage bImage = instance.getImageChallengeForID(id, locale);
// if limit reached reset captcha cache
if (captchaImgCache.size() > MAX_CACHE_SIZE) {
captchaImgCache = new HashMap<String, BufferedImage>();
}
captchaImgCache.put(id, bImage);
return bImage;
}
}
public static void resetImageChallengeForID(String id) {
if (captchaImgCache.containsKey(id)) {
captchaImgCache.remove(id);
}
}
}
when clicking on "Create Account" button Captcha is reset:
CustomerMB.openCreateCustomerAccount():
public String openCreateCustomerAccount() {
customerAccountEditVO = new CustomerAccountVO();
screenComponent.setPageName(NameConstants.CREATE);
getUserMB().resetCaptcha();
return null;
}
in UserMB.resetCaptcha():
public String resetCaptcha() {
CaptchaServiceSingleton.resetImageChallengeForID(JSFUtil.getRequest().getRequestedSessionId());
return null;
}
Perhaps it's not the perfect solution but at least it's working for all Browsers.

Related

How to refresh the meta tags in h:head?

In jsf2 when I have PROJECT_STAGE = Production my h:head part is not refreshed. When I change it to Development, then everything works fine. How to force the head part for being refreshed?
The h:head is in template.xhtml.
template.xhtml:
<h:head>
<meta name="description" content="#{metaBean.metaDescription}" />
<meta name="keywords" content="#{metaBean.metaKeywords}" />
</h:head>
web.xml
<context-param>
<param-name>javax.faces.PROJECT_STAGE</param-name>
<!-- <param-value>Development</param-value>-->
<param-value>Production</param-value>
</context-param>
<context-param>
<param-name>javax.faces.FACELETS_REFRESH_PERIOD</param-name>
<param-value>0</param-value>
</context-param>
...Or How to make dynamic meta description in other way?
Thank you
You can use meta filter like this
public class HtmlMetaFilter implements Filter {
private List<String> metaNames = null;
private List<String> metaValues = null;
private int metaSize = 0;
#Override
public void destroy() {
// logger.info("html meta filter destroyed.");
}
#Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletResponse res = (HttpServletResponse)response;
for(int i = 0; i < metaSize; i++) {
res.setHeader(metaNames.get(i), metaValues.get(i));
}
chain.doFilter(request, res);
}
#Override
public void init(FilterConfig filterConfig) throws ServletException {
metaNames = Configuration.getInstance().getList("//configuration/web-app/html-meta/name");
metaValues = Configuration.getInstance().getList("//configuration/web-app/html-meta/value");
if(metaNames != null && metaValues != null) {
metaSize = Math.min(metaNames.size(), metaValues.size());
}
// logger.info("html meta filter initialized.");
}
}
If you want to change it dynamically, then you need to set the different data in init method from your bean file.
I've found answer on this post: title in h:head rendered before render response phase?
It's enough to use event preRenderView and prepare necessary values in it.

show image from database, the image property is ok but show other imagen. Using Primefaces

I' m try to show some imagen from db in a datatable for each person, but the datatable show wrong image. I dont know where the app is geting the the imagen that is showing beacause in backing beans the imagen is other.
<p:column>
<p:graphicImage id="img2" value="#{listadoPersonasMB.fotoPersona}">
<f:param name="id2" value="#{item.idPersona}" />
</p:graphicImage>
</p:column>
backing bean
FacesContext context = FacesContext.getCurrentInstance();
if (context.getCurrentPhaseId() == PhaseId.RENDER_RESPONSE) {
return new DefaultStreamedContent();
} else {
String studentId = context.getExternalContext().getRequestParameterMap().get("id2");
FacesContext ctx = FacesContext.getCurrentInstance();
ValueExpression vex = ctx.getApplication().getExpressionFactory().createValueExpression(ctx.getELContext(), "#{controladorMB}", ControladorMB.class);
ControladorMB gMB = (ControladorMB) vex.getValue(ctx.getELContext());
return new DefaultStreamedContent(new ByteArrayInputStream(gMB.getPersonaBean().devuelveFotoPersona(Integer.parseInt(studentId))));}
The browser keep some image on cache I try this and work fine:
1- set disable cache. put this on facesconfig.xml
<lifecycle>
<phase-listener id="nocache">jsfUtil.CacheControlPhaseListener</phase-listener>
</lifecycle>
2- class code:
public class CacheControlPhaseListener implements PhaseListener {
#Override
public PhaseId getPhaseId() {
return PhaseId.RENDER_RESPONSE;
}
#Override
public void afterPhase(PhaseEvent event) {
}
#Override
public void beforePhase(PhaseEvent event) {
FacesContext facesContext = event.getFacesContext();
HttpServletResponse response = (HttpServletResponse) facesContext.getExternalContext().getResponse();
response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); // HTTP 1.1.
response.setHeader("Pragma", "no-cache"); // HTTP 1.0.
response.setDateHeader("Expires", 0); // Proxies.
}
3- reaload page
<p:dialog width="800" onHide="window.location.reload()" height="600" appendTo="#(body)" modal="true" widgetVar="dialogModi" header="Editar persona" id="atDlg">

JSF Login with HttpServletRequest

i found a solution in stackoverflow how to code a login in JSF using HttpServletRequest.
First things first, the login.xhtml:
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html">
<h:head>
<title>Login</title>
</h:head>
<h:body>
<h3>Login here</h3>
<h:form id="loginForm">
<h:outputLabel for="username" value="Username:" />
<h:inputText value="#{loginService.userName}" id="username" requried="true" />
<br/>
<h:outputLabel for="password" value="Password:" />
<h:inputSecret value="#{loginService.password}" id="password" requried="true" />
<br/>
<h:commandButton id="button" value="Login" action="#{loginService.doLogin}" />
<br/>
<h:commandLink action="#{navigationService.redirectToIndex}" value="Home" />
<br/>
<h:messages />
<br/>
</h:form>
</h:body>
The loginService:
#Named
#SessionScoped
public class LoginService implements Serializable {
private String userName = "";
private String password = "";
#Inject
private NavigationService navigationService = null;
#Inject
private String originalURL = "";
/**
*
*/
#PostConstruct
public void init() {
ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
this.originalURL = (String) externalContext.getRequestMap().get(RequestDispatcher.FORWARD_REQUEST_URI);
if(this.originalURL == null) {
this.originalURL = externalContext.getRequestContextPath() + navigationService.toIndex();
} else {
String originalQuery = (String) externalContext.getRequestMap().get(RequestDispatcher.FORWARD_QUERY_STRING);
if(originalQuery != null) {
this.originalURL += "?" + originalQuery;
}
}
}
/**
*
* #return
* #throws IOException
*/
public void doLogin() throws IOException {
FacesContext context = FacesContext.getCurrentInstance();
ExternalContext externalContext = context.getExternalContext();
HttpServletRequest request = (HttpServletRequest)externalContext.getRequest();
try {
request.login(this.userName, this.password);
User user = dao.findUserByUserName(userName);
externalContext.getSessionMap().put("user", user);
externalContext.redirect(this.originalURL);
} catch(ServletException e) {
context.addMessage(null, new FacesMessage("Unknown login"));
} catch (NoSuchUserException e) {
context.addMessage(null, new FacesMessage(e.getMessage()));
}
}
/**
*
* #return
* #throws IOException
*/
public void doLogout() throws IOException {
ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
externalContext.invalidateSession();
externalContext.redirect(externalContext.getRequestContextPath() + navigationService.toLogin());
}
// Getters and Setters
}
The only thing i still need to know is now:
Where can i define for which pages login is needed?
A suggested solution is: putting all the pages requiring logging under one place (folder, ex: "private_section"), and the pages that don't need it (public access) are to be put wherever in the project context except under the folder "private_section". Then you can use a simple filter to control accessing to the private region (to our folder), and through this pattern (first annotation) you can specify the region to be controlled :
// imports
#WebFilter("/private_section/*")
public class LoggingFilter implements Filter {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest)request;
HttpServletResponse res = (HttpServletResponse)response;
UserBean user = (UserBean) req.getSession().getAttribute("user");
if (user != null && user.isLoggedIn()){
chain.doFilter(request,response);
}
else res.sendRedirect(req.getContextPath()+"/index.xhtml");
}
// other overriden methods

JSF h:commandLink not work with filter

I create simple login system for my jsf project.
In project I create filter to check user login status.
If login, continue to requested page.
If not login or session is destroy, redirect to login page
My problem is, if I apply filter then all of h:commandLink not process anything after clicked. But when I remove filter, everything work well.
I try to use h:commandButton with fileter, then everthing work correctly.
How can I fixed this problem?
I research for a long time, but not found any solution.
Please help me!
Filter code:
#WebFilter(filterName = "AuthenticationFilter", urlPatterns = {"*.htm"}, dispatcherTypes = {DispatcherType.FORWARD, DispatcherType.REQUEST})
public class AuthenticationFilter implements Filter {
// The filter configuration object we are associated with. If
// this value is null, this filter instance is not currently
// configured.
private FilterConfig filterConfig = null;
#Inject
private AuthenticationManager authenticationManager;
public AuthenticationFilter() {
}
/**
* #param request The servlet request we are processing
* #param response The servlet response we are creating
* #param chain The filter chain we are processing
*
* #exception IOException if an input/output error occurs
* #exception ServletException if a servlet error occurs
*/
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
Throwable problem = null;
try {
HttpServletRequest req = (HttpServletRequest) request;
String requestUrl = req.getRequestURI();
String contextPath = req.getContextPath();
if(contextPath.equals("/")){
contextPath = "";
}
String jsfUrl = requestUrl.replaceFirst(contextPath, "");
if (authenticationManager.allowedAccess(jsfUrl) || requestUrl.equalsIgnoreCase(contextPath+"/login.htm")) {
chain.doFilter(request, response);
}
else {
String redirectPath = contextPath+"/login.htm";
((HttpServletResponse) response).sendRedirect(redirectPath); // Not logged in, so redirect to error page.
}
}
catch (Throwable t) {
// If an exception is thrown somewhere down the filter chain,
// we still want to execute our after processing, and then
// rethrow the problem after that.
problem = t;
}
// If there was a problem, we want to rethrow it if it is
// a known type, otherwise log it.
if (problem != null) {
if (problem instanceof ServletException) {
throw (ServletException) problem;
}
if (problem instanceof IOException) {
throw (IOException) problem;
}
sendProcessingError(problem, response);
}
}
/**
* Return the filter configuration object for this filter.
*/
public FilterConfig getFilterConfig() {
return (this.filterConfig);
}
/**
* Set the filter configuration object for this filter.
*
* #param filterConfig The filter configuration object
*/
public void setFilterConfig(FilterConfig filterConfig) {
this.filterConfig = filterConfig;
}
#Override
public void destroy() {
}
#Override
public void init(FilterConfig filterConfig) {
this.filterConfig = filterConfig;
if (filterConfig != null) {
}
}
}
Jsf code:
<h:body>
<f:view contentType="text/html" locale="#{authenticationManager.languageCode}">
<div class="header">
<h:form id="topForm" prependId="false">
<div class="logo">
<h1><img src="#{facesContext.externalContext.requestContextPath}/resources/images/login-logo2.png" width="220" height="64"/></h1>
</div>
<ul class="navTop">
<li>
<span class="pictograms">f</span>#{authenticationManager.currentUser.firstName} #{authenticationManager.currentUser.lastName}
</li>
<li>
<span class="pictograms">m</span>Messages
</li>
<li class="logout">
<h:commandButton action="#{authenticationManager.logout()}" value="aaaaaaa" style="color:#fff;" />
<h:commandLink action="#{authenticationManager.logout()}" value="#{label.Logout}"/>
<h:commandLink immediate="true" action="#{authenticationManager.logout}" id="logoutLink">
<span class="pictograms">E</span>***This link is not work correctly***
</h:commandLink>
</li>
</ul>
<ui:insert name="mainmenu"/>
</h:form>
</div>
Your concrete problem is caused because the serving of the JSF default resource jsf.js has been blocked by the filter. This resource is mandatory for functioning of JSF command links and JSF ajax requests (plain command buttons without ajax will just work).
You need to exclude JSF resources from the authentication check. You can do that by just checking if the request URI starts after the webapp context path with ResourceHandler.RESOURCE_IDENTIFIER (which has a value of /javax.faces.resource).
So, basically:
HttpServletRequest req = (HttpServletRequest) request;
if (req.getRequestURI().startsWith(req.getContextPath() + ResourceHandler.RESOURCE_IDENTIFIER)) {
chain.doFilter(request, response); // Let it continue.
return;
}
Checking the file extensions is clumsy and does not sufficiently cover all possible resource requests.
After research, I didn't found any solution.
So I guest to many possible ways, and found the explanation about this.
I filter everything with filter pattern "*.htm", then every request like .js.htm, .css.htm, .gif.htm, .jpg.htm will redirected to login.htm page.
The point is redirect .js.htm to login page instead of right file.
So the important library like jsf.js.htm was redirect to login.htm, this is the main cause to make h:commandLink work not correctly.
Hop this will help someone, like me.
try {
HttpServletRequest req = (HttpServletRequest) request;
String requestUrl = req.getRequestURI();
if(requestUrl.endsWith(".js.htm")
|| requestUrl.endsWith(".css.htm")
|| requestUrl.endsWith(".gif.htm")
|| requestUrl.endsWith(".png.htm")
|| requestUrl.endsWith(".jpg.htm")
|| requestUrl.endsWith(".jpeg.htm")){
chain.doFilter(request, response);
}
else{
String contextPath = req.getContextPath();
if(contextPath.equals("/")){
contextPath = "";
}
String jsfUrl = requestUrl.replaceFirst(contextPath, "");
if (authenticationManager.allowedAccess(jsfUrl) || requestUrl.equalsIgnoreCase(contextPath+"/login.htm")) {
chain.doFilter(request, response);
}
else {
String redirectPath = contextPath+"/login.htm";
((HttpServletResponse) response).sendRedirect(redirectPath); // Not logged in, so redirect to error page.
}
}
}

<p:graphicImage> not rendering image

I have a JSF page where I want to show a image. The image is stored in the database as a blob.
The entity looks like this:
#Entity
public class Player
{
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
private Long id;
#Lob
private byte[] pictureData;
#Transient
private StreamedContent streamedPicture;
public StreamedContent getStreamedPicture()
{
if (streamedPicture == null && pictureData != null)
{
try
{
ByteArrayOutputStream os = new ByteArrayOutputStream();
os.write(pictureData);
streamedPicture = new DefaultStreamedContent(
new ByteArrayInputStream(
os.toByteArray()),
"image/png");
}
catch (FileNotFoundException e)
{}
catch (IOException e)
{}
}
return streamedPicture;
}
}
The JSF page is this:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:p="http://primefaces.org/ui">
<h:head></h:head>
<body>
<ui:repeat var="player" value="#{playerbean.cachedPlayers}">
<h:outputText value="#{player.id}" />
<p:graphicImage value="#{player.streamedPicture}" rendered="#{player.streamedPicture != null}"/>
</ui:repeat>
</body>
</html>
And the bean I call looks like this:
#ManagedBean(name = "playerbean")
#SessionScoped
public class PlayerBean
implements Serializable
{
#EJB
private PlayerManager playerManager;
private List<Player> cachedPlayers;
public List<Player> getCachedPlayers()
{
if (cachedPlayers == null)
{
cachedPlayers = playerManager.getAll();
}
return cachedPlayers;
}
}
While debugging I set a breakpoint in PrimeResourceHandler in the method handleResourceRequest(). The code of the PrimeResourceHandler I'm looking at contains this:
try {
String dynamicContentEL = (String) session.get(dynamicContentId);
ELContext eLContext = context.getELContext();
ValueExpression ve = context.getApplication().getExpressionFactory().createValueExpression(context.getELContext(), dynamicContentEL, StreamedContent.class);
StreamedContent content = (StreamedContent) ve.getValue(eLContext);
HttpServletResponse response = (HttpServletResponse) context.getExternalContext().getResponse();
response.setContentType(content.getContentType());
byte[] buffer = new byte[2048];
int length;
InputStream inputStream = content.getStream();
while ((length = (inputStream.read(buffer))) >= 0) {
response.getOutputStream().write(buffer, 0, length);
}
response.setStatus(200);
response.getOutputStream().flush();
context.responseComplete();
} catch(Exception e) {
logger.log(Level.SEVERE, "Error in streaming dynamic resource.");
} finally {
session.remove(dynamicContentId);
}
When passing the line StreamedContent content = (StreamedContent) ve.getValue(eLContext); content appears to be null. This of course causes a NullPointerException. However in the JSF page I told the element not to render if the value is null.
The <p:graphicImage> component cannot have a value attribute that points to a managed property within a SessionScoped bean. The value must be set to a RequestScoped bean.
This is because an HTTP request for an image/jpeg content type HTTP response is inherently stateless. The browser will make the initial request for the JSF page content, then for every HTML <img> tag that is rendered it will make seperate requests to the dynamically generated url of each <img> tag to fetch these. Fetching an image in a stateful context doesn't really make sense.

Resources