0

I have created a custom component in JSF 2.0. Its purpose is to display a session attribute value in a input text box. I want the session attribute to be created in HttpSessionListener sessionCreated method. The problem is encodeAll method is getting invoked prior to sessionCreated method. What should I do to make sessionCreated invoked prior to encodeAll?

web.xml

<context-param>
    <param-name>javax.faces.FACELETS_LIBRARIES</param-name>
    <param-value>/WEB-INF/components.taglib.xml</param-value>
</context-param>
<listener>
    <listener-class>mycomp.jsf.listener.MyListener</listener-class>
</listener>

components.taglib.xml

<facelet-taglib xmlns="http://java.sun.com/xml/ns/javaee"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-facelettaglibrary_2_0.xsd"
            version="2.0">
<namespace>http://mycomp/jsf/components</namespace>
<tag>
    <tag-name>mycomp</tag-name>
    <component>
        <component-type>MyComp</component-type>
    </component>
</tag>
</facelet-taglib>

MyListener.java

public class MyListener implements HttpSessionListener {
@Override
public void sessionCreated(HttpSessionEvent event) {
    HttpSession session = event.getSession();
    session.setAttribute("sessionid", session.getId());
}
@Override
public void sessionDestroyed(HttpSessionEvent se) {
} 
}

MyComp.java

@FacesComponent(value = "MyComp")
public class MyComp extends UIComponentBase {
public MyComp() {
    setRendererType(null);
}
protected Class getComponentClass() {
    return this.getClass();
}
@Override
public void decode(FacesContext context) {
//some logic
}
@Override
public void encodeAll(FacesContext context) throws IOException {
    String clientId = getClientId(context) + ":token";
    HttpSession session = (HttpSession) context.getExternalContext().getSession(false);
    String sessionId = (String) session.getAttribute("sessionid");
    if (sessionId == null || "".equals(sessionId)) {
        throw new RuntimeException("sessionid is missing!");
    }
    ResponseWriter writer = context.getResponseWriter();
    writer.startElement("input", this);
    writer.writeAttribute("type", "text", "type");
    writer.writeAttribute("name", clientId, "name");
    writer.writeAttribute("value", sessionId, "value");
    writer.endElement("input");
}
@Override
public String getFamily() {
    return null;
}
}

index.xhtml

<html xmlns="http://www.w3.org/1999/xhtml"
  xmlns:h="http://java.sun.com/jsf/html"
  xmlns:t="http://mycomp/jsf/components">
<h:head>
</h:head>
<h:body>
    <h:form>
        <t:mycomp />
    </h:form>
</h:body>
</html>
Praneeth
  • 1,457
  • 5
  • 23
  • 36
  • Setting the session ID is purely exemplary, I assume? What's your concrete problem? Did you get a `NullPointerException` on the line `session.getAttribute()`? Or did you get a `RuntimeException` which you coded yourself there? – BalusC Apr 12 '12 at 21:10
  • I am getting a `NullPointerException` as `session` object is null. Does it mean `encodeAll` method is getting executed even before session is created? Anyways, my actual goal is to create a random number and put it in session scope to prevent CSRF. Similar code in JSF 1.2 had `sessionCreated` invoked prior to `encodeBegin`, so I didn't have this problem there. – Praneeth Apr 13 '12 at 13:44
  • You should have been more clear about that in the question. I already guessed that, but you wasn't been explicit about that. Exceptions should not be ignored as if they are decoration. I have posted an answer. – BalusC Apr 13 '12 at 14:01

1 Answers1

0

Replace getSession(false) by getSession(true).

HttpSession session = (HttpSession) context.getExternalContext().getSession(true);

The boolean tells whether the session should be created or not when it isn't created yet. With getSession(false) you're thus risking getting a null when it isn't been created at that point yet and hence the NullPointerException whenever you call any methods on it. See also the javadoc.


Unrelated to the concrete problem, cleaner is to use ExternalContext#getSessionMap() instead. Having "raw" javax.servlet imports in your JSF code namely usually indicates smells in the JSF code. I.e. something is been done unnecessarily overcomplicated. You should then look for simpler ways with pure JSF API.

Replace

HttpSession session = (HttpSession) context.getExternalContext().getSession(true);
String sessionId = (String) session.getAttribute("sessionid");
if (sessionId == null || "".equals(sessionId)) {
    throw new RuntimeException("sessionid is missing!");
}

by

String sessionId = (String) context.getExternalContext().getSessionMap().get("sessionid");
if (sessionId == null || "".equals(sessionId)) {
    throw new RuntimeException("sessionid is missing!");
}
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • Great!!! I thought of replacing `getSession(false)` with `getSession(true)` but was not sure if it fits well in my CSRF. I don't think I can use `ExternalContext#getSessionMap()` as `sessionId` will be always `null` – Praneeth Apr 13 '12 at 14:56
  • That would be a major bug in JSF impl used. It should certainly not be `null` if you set it that way. What JSF impl/version are you using? – BalusC Apr 13 '12 at 15:02