new CDI conversation - jsf

While inside a not trasient conversation, I need to start a new conversation for the bean.
The case is the following: I have a jsf page with a cdi bean to handle creation and altering of an order. On the menu of the page there is an item which is "new Order". So, when altering an Order, I need to click on "new Order" and the page must be refreshed with the new CID, and a new conversation scope. But if I try to do this, the conversation.getConverstaionId() always return the same value, even if I call conversation.end() and conversation.begin() first.
EDIT:
I have a page to edit an order. When clicking on a new button (of the menu), I want it to refresh and start a new conversation, to add a new order. So this button calls the method redirectToNewOrderPage(). But it has the problem described on the code and before.
#Named
#ConversationScoped
public class OrderEditBean implements Serializable {
private static final long serialVersionUID = 1L;
#Inject
private Conversation conversation;
[...]
public void redirectToNewOrderPage() {
String cid = createNewConversationId();
setOrder(null);
try {
FacesContext.getCurrentInstance().getExternalContext().redirect("/OrdersManager/restricted/orders/edit.xhtml?cid=" + cid);
} catch (IOException e) {
e.printStackTrace();
}
}
private String createNewConversationId() {
String oldConversationId = null;
String newConversationId = null;
oldConversationId = conversation.getId();
if (!conversation.isTransient() && conversation.getId() != null) {
conversation.end();
}
conversation.begin();
newConversationId = conversation.getId();
// **************
// at this point newConversationId is equal to
// oldConversationId if the conversation was NOT transient.
// **************
return newConversationId;
}
}

What you are trying to do, does not work. The conversation scope in CDI is not as power as the one from Seam 2 (if that's where you're coming from).

Related

My sessionscoped bean does not have values stored on every refresh

I have a SessionScoped Bean which consists of a list and a flag. The list populates a datatable in the xhtml view.
On first load, the list is populated. Every other load should not repopulate the list but it does despite the check. This behaviour also holds for postbacks on commandLinks clicks.
#ManagedBean(name = "customerSegmentInfo")
#SessionScoped
public class CustomerSegmentInfo extends BasePage implements Serializable {
private boolean isNBACalled = false;
private List<NextBestActionDTO> nextBestActionList = null;
public List<NextBestActionDTO> getNextBestActionList() {
log.debug("----- nbaCalled: " + isNBACalled);
if(nextBestActionList == null && !isNBACalled){
log.debug("-------- getNextBestAction");
try {
nextBestActionList = this.getNbaService().getNextBestAction(this.getCustomerInfo().getCountryCode(), this.getCustomerInfo().getCustomerNo());
log.debug("---- " + nextBestActionList.toString());
} catch (Exception e) {
// TODO Auto-generated catch block
log.debug("------ error calling nba service bean " + e);
}
isNBACalled = true;
log.debug("----- nbaCalled: " + isNBACalled);
}
return nextBestActionList;
}
public void setNextBestActionList(List<NextBestActionDTO> nextBestActionList) {
this.nextBestActionList = nextBestActionList;
}
}
As the list is not null from last page load and flag also set to true, the call getNextBestAction shouldnt be made, but in logs the flag is false and apparently the list is null too.
the behaviour is irregular
On certain loads it behaves as expected on others not.
I Tried setting the javax.faces.STATE_SAVING_METHOD context parameter in the web.xml to both server and client, nothing changed.
Thank you in advance

Bean doesn't dispatch to another page

I've a problem with a bean that doesn't dispatch the response to another page.
This is the code:
#ManagedBean(name = "ssoServiceBean")
public class SSOServiceBean {
#ManagedProperty(value="#{param.samlRequest}")
private String samlRequest;
#ManagedProperty(value="#{param.relayState}")
private String relayState;
#PostConstruct
public void submit() {
System.out.println("1) PostConstruct method called");
//samlRequest = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("samlRequest");
//relayState = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("relayState");
processResponse();
}
//getters and setters omitted for succinctness
private void processResponse(){
System.out.println("2) Processing response");
String uri;
if(samlRequest != null && !samlRequest.equals("") && relayState != null && !relayState.equals("")) {
System.out.println("SAMLRequest: "+samlRequest);
System.out.println("RelayState: "+relayState);
uri = "challenge.xhtml";
System.out.println("3) Sending challenge...");
} else {
uri = "dashboard.xhtml";
System.out.println("3) Sending dashboard...");
}
try {
FacesContext.getCurrentInstance().getExternalContext().dispatch(uri);
System.out.println("4) Done.");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
The problem is that the dispatch() method doesn't work properly, and seems to be ignored.
Infact the system responses with an error of the related bean's page ssoservice.xhtml
I've used the Postconstruct annotation because with this bean I've to intercept POST parameters that come from a third-party page.
Once I've received the post parameters, I've to render the challenge.xhtml page, WITHOUT using a redirect directive.
Nextly, the user will submit challenge.xhtml to the related bean ChallengeBean.java .
So, what is the problem? Why dispatch doesn't work?

How to destroy a session after data is presented in a jsf page

I have this problem: I´m making this wonderfull tutorial The NetBeans E-commerce Tutorial . But instead of make it in JSP as is presented, i´m making a JSF version. Just to undertands the logic in construction an application like that.
In certain part the ControllerServlet.java, has this code:
int orderId = orderManager.placeOrder(name, email, phone, address, cityRegion, ccNumber, cart);
// if order processed successfully send user to confirmation page
if (orderId != 0) {
// dissociate shopping cart from session
cart = null;
// end session
session.invalidate();
// get order details
Map orderMap = orderManager.getOrderDetails(orderId);
// place order details in request scope
request.setAttribute("customer", orderMap.get("customer"));
request.setAttribute("products", orderMap.get("products"));
request.setAttribute("orderRecord", orderMap.get("orderRecord"));
request.setAttribute("orderedProducts", orderMap.get("orderedProducts"));
userPath = "/confirmation";
// otherwise, send back to checkout page and display error
As you can see, the author invalidates the session, in order to permit another purchase order. I made an Managed Bean with session scope in order to mantain the data avalaible throught the whole session. But when I try to clean up the session, as in the tutorial the author does, I can´t receive the data for confirmation.
Then, I made a different managed bean in order to have one to process the order (CartManagerBean), and another one to present the confirmation (ConfirmationMBean). I just injected the confirmatioBean into the cartBean to pass the orderId, necessary to present the data. In the confirmationBean, I made a cleanUp() method that invalidates the session.
But always, the data is not presented. So if any one can tell me what to do, I´ll appreciate.
Here is the part of my cartBean's code that pass the data to the confirmation bean:
...
#ManagedProperty(value ="#{confirmationBean}")
private ConfirmationMBean confirmationBean;
...
public String makeConfirmation() {
FacesContext fc = FacesContext.getCurrentInstance();
if (!cartMap.isEmpty()) {
int orderId = orderManager.placeOrder(name, email, phone, address, credicard, cartMap);
// if order processed successfully send user to confirmation page
if (orderId != 0) {
// get order details
confirmationBean.setOrderId(orderId);
// dissociate shopping cart from session
cartMap.clear();
// end session
//fc.getExternalContext().invalidateSession();
}
}
return "confirmation";
}
As you can see, I commented the part that invalidates the session. Here is the code that I implemented for the ConfirmationMBean:
#ManagedBean(name = "confirmationBean")
#SessionScoped
public class ConfirmationMBean implements Serializable{
private Customer customer;
private List<OrderedProduct> orderedProducts;
private CustomerOrder orderRecord;
private List<Product> products;
private int orderId;
#EJB
private OrderManager orderManager;
public void cleanUp(){
FacesContext fc = FacesContext.getCurrentInstance();
fc.getExternalContext().invalidateSession();
}
private void init(){
Map<String, Object> orderMap = orderManager.getOrderDetails(orderId);
customer = (Customer) orderMap.get("customer");
orderRecord = (CustomerOrder) orderMap.get("orderRecord");
orderedProducts = (List<OrderedProduct>) orderMap.get("orderedProducts");
products = (List<Product>) orderMap.get("products");
}
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
public List<OrderedProduct> getOrderedProducts() {
return orderedProducts;
}
public void setOrderedProducts(List<OrderedProduct> orderedProducts) {
this.orderedProducts = orderedProducts;
}
public CustomerOrder getOrderRecord() {
return orderRecord;
}
public void setOrderRecord(CustomerOrder orderRecord) {
this.orderRecord = orderRecord;
}
public List<Product> getProducts() {
return products;
}
public void setProducts(List<Product> products) {
this.products = products;
}
public int getOrderId() {
return orderId;
}
public void setOrderId(int orderId) {
this.orderId = orderId;
init();
cleanUp();
}
}
As you can see, when the orderId is setted by the preceding bean, the data is requested from the database, and populates the variables to present in the facelet. ¿Where or how I have to use the cleanUp method in order to obtain the same result that the tutorial?
Thanks in advance.
Put the bean where you're invoking the action in the request scope instead of session scope and get hold of the desired session scoped bean as a (managed) property.
#ManagedBean
#RequestScoped
public class SubmitConfirmationBean {
#ManagedProperty("#{cartBean}")
private CartBean cartBean;
// ...
}
And reference it by #{submitConfirmationBean.cartBean...} instead of #{cartBean...}.
Alternatively, explicitly put the desired session scoped bean in the request scope in the same action method as where you're invalidating the session:
externalContext.getRequestMap().put("cartBean", cartBean);
This way the #{cartBean...} will refer the request scoped one instead of the session scoped one which is newly recreated at that point because you destroyed the session. The request scoped one is lost by next request anyway.

How to save all FacesMessages over different requests

I was just wondering to implement a kind of log display to user where in all messages in the application are displayed to user all the time.
Since I use JSF 1.2 and RF 3.3.3, wanted to know if it is possible to save all messages be it added by different requests and display them to user, so that user will know the history of actions he has done. It is also useful for support team to analyse the cause of the problem and he can send the messages to developer if it needs to simulated or to debug purpose also.
I also know facesmessages get cleared over different requests, thats where my question lies, to save messages over different requests :)
Could be handled in different ways by saving them is a session variable or so...I would appreciate all possible answers
You could collect all messages during the render response in a PhaseListener. E.g.
public class MessagesListener implements PhaseListener {
#Override
public PhaseId getPhaseId() {
return PhaseId.RENDER_RESPONSE;
}
#Override
public void beforePhase(PhaseEvent event) {
FacesContext context = event.getFacesContext();
Iterator<String> clientIds = context.getClientIdsWithMessages();
while (clientIds.hasNext()) {
String clientId = clientIds.next();
Iterator<FacesMessage> messages = context.getMessages(clientId);
while (messages.hasNext()) {
FacesMessage message = messages.next();
save(clientId, message); // Do your job here.
}
}
}
#Override
public void afterPhase(PhaseEvent event) {
// NOOP.
}
}
To get it to run, register it as follows in faces-config.xml:
<lifecycle>
<phase-listener>com.example.MessagesListener</phase-listener>
</lifecycle>

JSF Active Sessions counter. How to?

Good evening,
In a test JSF 2.0 web app, I am trying to get the number of active sessions but there is a problem in the sessionDestroyed method of the HttpSessionListener.
Indeed, when a user logs in, the number of active session increases by 1, but when a user logs off, the same number remains as it is (no desincrementation happens) and the worse is that, when the same user logs in again (even though he unvalidated the session), the same number is incremented.
To put that in different words :
1- I log in, the active sessions number is incremented by 1.
2- I Logout (the session gets unvalidated)
3- I login again, the sessions number is incremented by 1. The display is = 2.
4- I repeat the operation, and the sessions number keeps being incremented, while there is only one user logged in.
So I thought that method sessionDestroyed is not properly called, or maybe effectively called after the session timeout which is a parameter in WEB.XML (mine is 60 minutes).
That is weird as this is a Session Listener and there is nothing wrong with my Class.
Does someone please have a clue?
package mybeans;
import entities.Users;
import java.io.*;
import java.util.Date;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.faces.bean.ManagedBean;
import javax.faces.context.FacesContext;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
import jsf.util.JsfUtil;
/**
* Session Listener.
* #author TOTO
*/
#ManagedBean
public class SessionEar implements HttpSessionListener {
public String ctext;
File file = new File("sessionlog.csv");
BufferedWriter output = null;
public static int activesessions = 0;
public static long creationTime = 0;
public static int remTime = 0;
String separator = ",";
String headtext = "Session Creation Time" + separator + "Session Destruction Time" + separator + "User";
/**
*
* #return Remnant session time
*/
public static int getRemTime() {
return remTime;
}
/**
*
* #return Session creation time
*/
public static long getCreationTime() {
return creationTime;
}
/**
*
* #return System time
*/
private String getTime() {
return new Date(System.currentTimeMillis()).toString();
}
/**
*
* #return active sessions number
*/
public static int getActivesessions() {
return activesessions;
}
#Override
public void sessionCreated(HttpSessionEvent hse) {
// Insert value of remnant session time
remTime = hse.getSession().getMaxInactiveInterval();
// Insert value of Session creation time (in seconds)
creationTime = new Date(hse.getSession().getCreationTime()).getTime() / 1000;
if (hse.getSession().isNew()) {
activesessions++;
} // Increment the session number
System.out.println("Session Created at: " + getTime());
// We write into a file information about the session created
ctext = String.valueOf(new Date(hse.getSession().getCreationTime()) + separator);
String userstring = FacesContext.getCurrentInstance().getExternalContext().getRemoteUser();
// If the file does not exist, create it
try {
if (!file.exists()) {
file.createNewFile();
output = new BufferedWriter(new FileWriter(file.getName(), true));
// output.newLine();
output.write(headtext);
output.flush();
output.close();
}
output = new BufferedWriter(new FileWriter(file.getName(), true));
//output.newLine();
output.write(ctext + userstring);
output.flush();
output.close();
} catch (IOException ex) {
Logger.getLogger(SessionEar.class.getName()).log(Level.SEVERE, null, ex);
JsfUtil.addErrorMessage(ex, "Cannot append session Info to File");
}
System.out.println("Session File has been written to sessionlog.txt");
}
#Override
public void sessionDestroyed(HttpSessionEvent se) {
// Desincrement the active sessions number
activesessions--;
// Appen Infos about session destruction into CSV FILE
String stext = "\n" + new Date(se.getSession().getCreationTime()) + separator;
try {
if (!file.exists()) {
file.createNewFile();
output = new BufferedWriter(new FileWriter(file.getName(), true));
// output.newLine();
output.write(headtext);
output.flush();
output.close();
}
output = new BufferedWriter(new FileWriter(file.getName(), true));
// output.newLine();
output.write(stext);
output.flush();
output.close();
} catch (IOException ex) {
Logger.getLogger(SessionEar.class.getName()).log(Level.SEVERE, null, ex);
JsfUtil.addErrorMessage(ex, "Cannot append session Info to File");
}
}
} // END OF CLASS
I am retrieving the active sessions number this way:
<h:outputText id="sessionsfacet" value="#{UserBean.activeSessionsNumber}"/>
from another managedBean:
public String getActiveSessionsNumber() {
return String.valueOf(SessionEar.getActivesessions());
}
My logout method is as follow:
public String logout() {
HttpSession lsession = (HttpSession) FacesContext.getCurrentInstance().getExternalContext().getSession(false);
if (lsession != null) {
lsession.invalidate();
}
JsfUtil.addSuccessMessage("You are now logged out.");
return "Logout";
}
// end of logout
I'm not sure. This seems to work fine for a single visitor. But some things definitely doesn't look right in your HttpSessionListener.
#ManagedBean
public class SessionEar implements HttpSessionListener {
Why is it a #ManagedBean? It makes no sense, remove it. In Java EE 6 you'd use #WebListener instead.
BufferedWriter output = null;
This should definitely not be an instance variable. It's not threadsafe. Declare it methodlocal. For every HttpSessionListener implementation there's only one instance throughout the application's lifetime. When there are simultaneous session creations/destroys, then your output get overridden by another one while busy and your file would get corrupted.
public static long creationTime = 0;
public static int remTime = 0;
Those should also not be an instance variable. Every new session creation would override it and it would get reflected into the presentation of all other users. I.e. it is not threadsafe. Get rid of them and make use of #{session.creationTime} and #{session.maxInactiveInterval} in EL if you need to get it over there for some reason. Or just get it straight from the HttpSession instance within a HTTP request.
if (hse.getSession().isNew()) {
This is always true inside sessionCreated() method. This makes no sense. Remove it.
JsfUtil.addErrorMessage(ex, "Cannot append session Info to File");
I don't know what that method exactly is doing, but I just want to warn that there is no guarantee that the FacesContext is present in the thread when the session is about to be created or destroyed. It may take place in a non-JSF request. Or there may be no means of a HTTP request at all. So you risk NPE's because the FacesContext is null then.
Nonetheless, I created the following test snippet and it works fine for me. The #SessionScoped bean implicitly creates the session. The commandbutton invalidates the session. All methods are called as expected. How many times you also press the button in the same browser tab, the count is always 1.
<h:form>
<h:commandButton value="logout" action="#{bean.logout}" />
<h:outputText value="#{bean.sessionCount}" />
</h:form>
with
#ManagedBean
#SessionScoped
public class Bean implements Serializable {
public void logout() {
System.out.println("logout action invoked");
FacesContext.getCurrentInstance().getExternalContext().invalidateSession();
}
public int getSessionCount() {
System.out.println("session count getter invoked");
return SessionCounter.getCount();
}
}
and
#WebListener
public class SessionCounter implements HttpSessionListener {
private static int count;
#Override
public void sessionCreated(HttpSessionEvent event) {
System.out.println("session created: " + event.getSession().getId());
count++;
}
#Override
public void sessionDestroyed(HttpSessionEvent event) {
System.out.println("session destroyed: " + event.getSession().getId());
count--;
}
public static int getCount() {
return count;
}
}
(note on Java EE 5 you need to register it as <listener> in web.xml the usual way)
<listener>
<listener-class>com.example.SessionCounter</listener-class>
</listener>
If the above example works for you, then your problem likely lies somewhere else. Perhaps you didn't register it as <listener> in web.xml at all and you're simply manually creating a new instance of the listener everytime inside some login method. Regardless, now you at least have a minimum kickoff example to build further on.
Something in a completely different direction - tomcat supports JMX. There is a JMX MBean that will tell you the number of active sessions. (If your container is not tomcat, it should still support JMX and provide some way to track that)
Is your public void sessionDestroyed(HttpSessionEvent se) { called ? I don't see why it won't increment. After the user calls session.invalidate() through logout, the session is destroyed, and for the next request a new one is created. This is normal behavior.

Resources