The response was already committed by the time we tried to set the outgoing cookie for the flash. Any values stored to the flash will not be available - jsf

I have one view scoped JSF managed bean. In the action listener method of <p:commandButton> in this managed bean, I'm redirecting to another page as follows.
public void register() {
ExternalContext context = FacesContext.getCurrentInstance().getExternalContext();
Flash flash = context.getFlash();
flash.setKeepMessages(true);
flash.put("emailId", emailId);
context.redirect(((HttpServletRequest) context.getRequest()).getContextPath()+"/page.jsf");
}
page.jsf is bound to a request scoped bean in which I'm receiving the email address stored in a flash scope as follows.
<ui:define name="metaData">
<f:metadata>
<f:viewAction action="#{requestScopedBean.init}"/>
</f:metadata>
</ui:define>
public void init() {
String emailId = (String) FacesContext.getCurrentInstance().getExternalContext().getFlash().get("emailId");
System.out.println("emailId = "+emailId);
}
The email address is unavailable in the target bean. The email address in the target bean is required just to show the user who has registered to indicate that a message has been sent to that address so that the email address can be verified.
It produces the following warning on the server terminal.
WARNING: JSF1095: The response was already committed by the time we
tried to set the outgoing cookie for the flash. Any values stored to
the flash will not be available on the next request.
Why does this happen? My Mojarra version is 2.2.7

Related

JSF / primefaces Session - Session gets lost

I´m new at JSF programming and got a problem with my login/session which gets lost after the login.
I want to implement an easy login where a user can type in username and password. So I wrote a LoginController:
#ManagedBean
#SessionScoped
public class LoginController extends AbstractController{
#PostConstruct
public void initialiseSession() {
FacesContext.getCurrentInstance().getExternalContext().getSession(true);
}
private String username = "null";
private String password;
private boolean loggedIn = false;
#Inject
private EmployeeService employeeService;
public static final String employeeSessionKey = "user";
public LoginController() {
}
public String login() {
//check username and password and if true redirect to "/"
}
My login.xhtml looks like:
<h:form id="loginForm">
<h:outputLabel style="font-size:24px" value="Bitte melden Sie sich an!"/>
<p:panelGrid columns="2">
<p:outputLabel id="userOutput" for="userInput" value="Benutzername"/>
<p:inputText id="userInput" value="#{loginController.username}"></p:inputText>
<p:outputLabel id="passwordOutput" for="passwordInput" value="Passwort"/>
<p:inputText id="passwordInput" type="password" value="#{loginController.password}"></p:inputText>
<h:outputText value="Logindaten merken?" id="outputRememberLogin">
<p:selectBooleanCheckbox id="loginCheckbox">
</p:selectBooleanCheckbox>
<p:spacer width="10" id="loginFormLittleSpacer"></p:spacer>
</h:outputText>
<p:commandButton id="loginButton" value="Anmelden" action="#{loginController.login()}" ajax="false" >
</p:commandButton>
</p:panelGrid>
</h:form>
So when I login the redirect works. But when I go to another .xhtml page the session gets lost.
To test this, I put
<p:outputLabel value="#{loginController.username}"/>
on my pages. After the login, the username becomes "null".
I´m going crazy on this problem.
Any ideas?
thanks before.
Your LoginController bean looks like its annotations are okay and the code looks like it should work. However, there's a couple other things that you may want to check. Some of this may be obvious but your question is missing a few details so I’m not sure what level of experience you may have and where to start in this answer. Therefore, I’m starting from the beginning (almost)…
1. Domain Name Configuration
To use sessions, you must use a qualified domain name. Sending a request to an IP address will not allow sessions to work since client browsers only send session information to a fully qualified domain name (http://example.com/). If you’re calling your web app with an IP (such as ‘http://127.0.0.1:8080/MyApp’), the session data will never be sent to your web app and you will have a new session created with each request. Make sure you’re using a fully qualified domain name and path with each request to your application, for example ‘http://localhost:8080/MyApp’.
2. Application Configuration
Check that your web application’s <session-config> configuration is setup correctly. The default config should allow your code to work without having to add anything specific, so if you didn’t add anything, don’t worry about this. However, you may want to make sure there’s nothing that may be preventing the sessions from being reused.
Session cookies should be enabled (<tracking-mode>COOKIE</tracking-mode>)
Timeout should be long enough to not expire before the second request (<session-timeout>60</session-timeout>)
The cookie path should be correctly set for your use (<path>/</path>)
The following is a common session config that I use…
<session-config>
<session-timeout>60</session-timeout>
<tracking-mode>COOKIE</tracking-mode>
<cookie-config>
<path>/</path>
<http-only>true</http-only>
<secure>false</secure>
</cookie-config>
</session-config>
3. Session Cookie Tracking on the Client
If the above points don’t resolve the issue, you can really start diving into the session tracking by monitoring the request and response traffic between your app and the client browser. The session info is passed back and forth by a cookie (or query string parameter if cookies are disabled) named ‘JSESSIONID’. Its value will be the ID of the unique session and must be the same for each request to ensure that your web application tracks the same session. The below highlights some of the things to look for…
Make sure the ‘JSESSIONID’ session cookie is sent to the client browser correctly and is being sent back with each subsequent request. You can do this with Chrome or Safari's web developer tools (under the 'Network' tab) or with a separate utility, such as Wireshark.
Make sure the 'JSESSIONID' cookie exists in the response from the first request… this will let you know that your web app is at least creating the session and response cookie.
Make sure the second request from the client browser is passing the 'JSESSIONID' cookie back... this is the only way your web app knows which existing session to use.
Make sure the 'JESSIONID' cookie has a path of '/' (which may display as 'N/A' in the browser) or the path of your web app ('/MyApp', for example)… the client browser will only send cookies to the domain(s) and path associated with each. For example, if your login page is 'http://example.com/MyApp/login', the 'JSESSIONID' cookie may have a path of '/MyApp' (by default), which will not be returned if the following request is made to 'http://example.com/' (without the '/MyApp' path). The default path is the name of your web app (‘/MyApp’) and can be changed using the <path> config item indicated above.
4. Session Management within the Bean (Additional Information)
In your initialiseSession() method, the session object returned by FacesContext.getCurrentInstance().getExternalContext().getSession(true); isn't being saved and this line is essentially doing nothing. The #SessionScoped annotation will have already created a session and added the ‘JSESSIONID’ cookie to the response before initialiseSession() is called. Therefore, the only reason to call getSession(true) is if you want to save the session to a private object within the bean, which would be like this...
#ManagedBean
#SessionScoped
public class LoginController extends AbstractController{
// Create a global, private member for storing the session data...
private HttpSession session;
#PostConstruct
public void initialiseSession() {
// Assign the session to the global member…
session = FacesContext.getCurrentInstance().getExternalContext().getSession(true);
}
…
Again, the call to the getSession(true) isn’t necessary in your example since the SessionScoped bean will have already created the session. The above code is only necessary if you intended to update or use the session object, for example add an attribute or modify a setting.
In summary
Double-check the above points 1 – 3. My assumption would be that the ‘JSESSIONID’ cookie isn’t being sent back in subsequent requests. If you can confirm that the ‘JSESSIONID’ cookie (with the same value) is being included in each request then the problem isn't related to the session and may be related to the code in your login() method.
i used cookies for repair this kind of problems, in my application. inside Faceutils doesn't work before servlet.

Access ViewScoped ManagedBean from Servlet

Background information: I have a file upload applet in my jsf page. This applet expects an adress where it can send it's POST request. (I can't edit this post request to add more fields or something). The post method of my servlet then stores the file. This job can't be done by a managed bean because the servlet has to be annotated with #MultiPartConfig and I can't add this annotation to the jsf managed bean. In order to force the upload applet to use the same session I added an URL attribute named jsessionId to the post request according to this post. In my servlet I am now able to access session scoped beans.
Now I have a ViewScoped bean where I store some form input data which I want to use in the servlet, since adding those inputs to the post request doesn't work (Applet is a third party project (JUploadApplet) and for some reason it doesn't work to add additional form data).
Now is it possible to access the ViewScoped bean from within the servlet ? If I change the scope into SessionScope I am able to process the input but with ViewScoped I get a NullPointerException if I try to access the bean like this :
UploadBean uploadBean = (UploadBean)request.getSession().getAttribute("uploadBean");
This is not possible. Your best bet is to let the view scoped bean generate an unique key, store itself in the session scope by that key and pass that key as additional parameter to the applet and finally let the servlet access the session attribute by that key.
E.g.
private String sessionKey;
#PostConstruct
public void init() {
sessionKey = UUID.randomUUID().toString();
FacesContext.getCurrentInstance().getExternalContext().getSessionMap().put(sessionKey, this);
}
#PreDestroy
public void destroy() {
FacesContext.getCurrentInstance().getExternalContext().getSessionMap().remove(sessionKey);
}
Let the applet pass the sessionKey as request parameter to the servlet, so that the servlet can do
String sessionKey = request.getParameter("sessionKey");
Bean bean = (Bean) request.getSession().getAttribute(sessionKey);
// ...
Note that instead of the bean itself, you can also just store an arbitrary bean/valueobject/etc.

Understanding HttpServletRequest and cookies in JSF

In order to create a "Remember me" login in JSF, I am trying to understand how Cookies work. I have created a brand new Web Application using JSF, with this bean that creates a Cookie expiring with the session:
CookieBean class
#ManagedBean
#ViewScoped
public class CookieBean implements Serializable {
public void create() {
ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();
ec.addResponseCookie("MyTestCookie", "Hello Cookie", null);
}
}
and index.xhtml has this body:
<h:form>
<h:commandButton value="Create Cookie!" action="#{cookieBean.create()}" >
<f:ajax render="#form" />
</h:commandButton>
<p></p>
<h:outputText value="Cookie value: #{cookie['MyTestCookie'].value}" />
</h:form>
As a result, when the page first loads, there is no cookie, correctly, because it's the first time the application runs, and no cookie is there.
After clicking the button once, no cookie is displayed. Why? The button invokes the cookieBean#create() method, and the ajax tag should force a revaluation of the outputText component. And this should generate an HttpSerlvetRequest with the cookie... or not? The cookie shows only after I press the button again!.
More surprisingly, when I press the refresh button of the browser, the cookie is not shown, although I'd expect to see it, because the older session is still alive.
It's like if (re)loading the page doesn't send an HttpServletRequest to the server...
The #{cookie} refers to the cookies of the current HTTP request. If you add a new cookie, then it appears only on the HTTP response, but the HTTP request which is associated with this HTTP response does of course not have the cookie yet. It's only present in the subsequent request, depending on the age of the cookie.
You basically need to send a redirect afterwards to make the cookie available during rendering the HTTP response for the HTTP request.
As to the refresh matter, the request was most likely re-executed in the browser cache.

Is there any easy way to preprocess and redirect GET requests?

I'm looking for a best practise answer. I want to do some preprocessing for GET requests. So e.g. if the user is not allowed to see the page, redirect him to another page. But I don't want to use normal servlet filter, because I would like to express this behavior in the faces-config.xml. Is this possible and how is that called, how can it be done?
Can I define some Filter bean that also returns a String telling the faces-config.xml where to go next?
I googled for this but only hit on the normal filters. If I use filters, can a #WebFilter be a #ManagedBean at the same time? Or is that bad style?
If you're homegrowing HTTP request authentication on top of JSF, then a servlet filter is really the best approach. JSF is "just" a MVC framework and nothing in the JSF API is been specified to filter incoming HTTP requests to check user authentication. On normal GET requests, a JSF managed bean is usually only constructed when the HTTP response is about to be created and sent, or maybe already is been committed. This is not controllable from inside the managed bean. If the response is already been committed, you would not be able anymore to change (redirect) it. Authentication and changing the request/response really needs to be done far before the response is about to be sent.
If you were not homegrowing authentication, then you could have used the Java EE provided container managed authentication for this which is to be declared by <security-constraint> entries in web.xml. Note that this is also decoupled from JSF, but it at least saves you from homegrowing a servlet filter and a managed bean.
The general approach is to group the restricted pages behind a certain URL pattern like /app/*, /private/*, /secured/*, etc and to take the advantage of the fact that JSF stores session scoped beans as HttpSession attributes. Imagine that you've a JSF session scoped managed bean UserManager which holds the logged-in user, then you could check for it as follows:
#WebFilter(urlPatterns={"/app/*"})
public class AuthenticationFilter implements Filter {
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
HttpSession session = request.getSession(false);
UserManager userManager = (session != null) ? (UserManager) session.getAttribute("userManager") : null;
if (userManager == null || !userManager.isLoggedIn()) {
response.sendRedirect(request.getContextPath() + "/login.xhtml"); // No logged-in user found, so redirect to login page.
} else {
chain.doFilter(req, res); // Logged-in user found, so just continue request.
}
}
// ...
}
If you're using JSF 2.2+, there's another way to control the response right before it is been sent. You can make use of the <f:viewAction>. Put the following somewhere in your view:
<f:metadata>
<f:viewAction action="#{authenticator.check}" />
</f:metadata>
with
#Named
#RequestScoped // Scope doesn't matter actually. The listener will always be called on every request.
public class Authenticator {
public String check() {
if (authenticated) {
return null;
}
else {
return "login?faces-redirect=true";
}
}
// ...
}
This is guaranteed to be fired before the response is to be rendered. Otherwise when you do the job in e.g. #PostConstruct, then you may risk java.lang.IllegalStateException: response already committed when the bean is created for the first time when the response has already partially been rendered (and committed).
I only wouldn't consider it to be a "best" practice when it comes to handling HTTP authentication. It makes it too tight coupled into JSF. You should really keep using a servlet filter. But for other purposes, it may be fine.
See also:
When to use f:viewAction / preRenderView versus PostConstruct?
What can <f:metadata>, <f:viewParam> and <f:viewAction> be used for?
Limitations of using a PhaseListener instead of a Servlet Filter for authorization

Do CDI Events observed across session scoped JSF backing beans

I'm wondering if it is possible to observe a CDI event with multiple JSF 2.0 session scoped backing beans. I thought that I could push event/data to multiple sessions by observing the event.
I've set up a small test that allows the user to fire the event using a button on the page (which is tied to a method in the session scoped backing bean that actually fires the event). I thought that if I opened two different browsers, two sessions would be created and the event would notify each of the session scoped backing beans.
However, when running my little test and clicking the button to fire the event on one of the browsers, I see that the event only notifies one of the session scoped beans. It only notifies the bean from which the event was fired (i.e. - if I click the button in browser 1 the session scoped bean backing the session in browser 1 is notified, and if I click the button in browser 2 the bean backing the session in browser 2 is notified).
I was under the impression that events would notify all bean instances. However, this doesn't seem to be the case. Should I be able to do this? Do I just have something setup wrong?
UPDATE to show what my code looks like:
A snippet of jsfpage.xhtml to fire an event and show the session-scoped data:
<h:form>
<h:outputText value="#{BackingBean.property}" />
<p:commandButton value="Fire Event" action="#{EventFirer.fireEvent}" />
</h:form>
The Session-scoped bean that receives event:
#Named
#SessionScoped
public class BackingBean implements Serializable {
private String property;
public String getProperty() {
return property
}
public void listenForChange(#Observes EventObj event) {
logger.log(Level.INFO, "Event received");
property = event.toString();
}
}
An application scoped bean to fire the event:
#Named
#ApplicationScoped
public class EventFirer implements Serializable {
#Inject
private Event<EventObj> events;
public String fireEvent() {
logger.log(Level.INFO, "Event fired");
events.fire(new EventObj());
return null;
}
}
First, you'd better specify the type of the event:
#Inject
private Event<EventObj> events;
Apart from that there is no indication in the spec that would limit the bean instances on which the observer method is invoked. I'd file an issue about this (in the bugtracker of the implementation you are using. Perhaps Weld?)
I've found that all registered observers are fired.
Most notably if I have an observer on a Conversation Scoped bean and that bean is not active in the current Conversation then, when the event is fired, a new instance is created specially to receive it!

Resources