SessionScoped controller not working - jsf

EDIT:
Okay, so I tried setting a few console.writes to check what's happening... It seems my logout script is called upon navigation. But I don't call it anywhere except on my logout button.
Here is my template code:
<div class="navbar">
<div class="navbar-inner">
<ul class="nav">
<li class="active">Home</li>
<li>Races</li>
<li>Horses</li>
<h:panelGroup rendered="#{memberController.logged == true}">
<li>History</li>
<li>Logout</li>
</h:panelGroup>
<h:panelGroup rendered="#{memberController.logged == false}">
<li>Login</li>
<li>Create Account</li>
</h:panelGroup>
</ul>
</div>
</div>
Original message:
I'm creating a website for my school project (Java EE)... It's our first year doing so.
Now as this is evening school and only had a semester learning it, you might see that my way of doing things ain't the best out there :)
So to get started, I'm trying to create a login feature but instead of those hundered lines of security codes, we may use a simple session scoped member object.
So here you have a few of my classes:
Member class:
#Entity
#Table(name = "members")
public class Member implements Serializable {
//+ Getters, setters, HashCode and equals
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
private long id;
private double money;
#NotNull(message = "Username cannot be null")
#Size(min = 4, message = "Username should be of minimum 4 characters")
private String userName;
#NotNull(message = "Password cannot be null")
#Size(min = 4, message = "Password should be of minimum 4 characters")
private String password;
#PostPersist
private void initDefault() {
this.money = 500;
}
}
MemberBean class:
#Stateless
public class MemberBean {
#PersistenceContext(unitName="HorseRacingPU")
private EntityManager em;
public Member getMember(long id){
return em.find(Member.class, id);
}
public Member getMember(String username, String password){
TypedQuery<Member> q = em.createQuery("SELECT u FROM Member u WHERE u.userName=?1 AND u.password=?2", Member.class);
q.setParameter(1, username);
q.setParameter(2, password);
return q.getSingleResult();
}
public List<Member> getAllMembers(){
TypedQuery<Member> q = em.createQuery("SELECT u FROM Member u", Member.class);
return q.getResultList();
}
public Member addOrUpdateMember(Member u){
Member original = em.find(Member.class, u.getId());
if(original == null){
em.persist(u);
return u;
}else{
return em.merge(u);
}
}
public Member deleteMember(long id){
Member original = em.find(Member.class, id);
if(original != null){
em.remove(original);
}
return original;
}
}
MemberController class:
#SessionScoped
public class MemberController implements Serializable {
#EJB
private MemberBean bean;
private String username;
private String password;
private Member member;
private boolean logged = false;
// + their getters and setters
public List<Member> getAllMembers() {
return bean.getAllMembers();
}
public String login() {
member = bean.getMember(username, password);
if (member != null) {
logged = true;
return "/races/list.xhtml?faces-redirect=true";
}
return "/users/login.xhtml?faces-redirect=true";
}
public String logout() {
FacesContext.getCurrentInstance().getExternalContext().invalidateSession();
return "/index.xhtml?faces-redirect=true";
}
public void checkLogin(ComponentSystemEvent e) {
if (!logged) {
FacesContext context = FacesContext.getCurrentInstance();
ConfigurableNavigationHandler handler = (ConfigurableNavigationHandler) context.getApplication().getNavigationHandler();
handler.performNavigation("/users/login.xhtml?faces-redirect=true");
}
}
public Member getMember() {
return member;
}
public void submit() {
bean.addOrUpdateMember(member);
}
}
The main error I'm getting is the following:
INFO: Exception when handling error trying to reset the response.
A more specific detail error can be found here: http://pastebin.com/h5nTNnes
So what happens is that when I login, everything works great. The moment I navigate to another url (after being forwarded to /races/list) I get logged out. The error itself shows when I use the checkLogin():
<f:event type="preRenderView" listener="#{memberController.checkLogin}" />
I'm not sure whether this is related, but when I login without any demo data (or with wrong credentials) I get an evaluation exception and that no entity could be retrieved.
Here more details: http://pastebin.com/Tv9mQ1K9
What could this be? I scratched my head for 3 days now and can't seem to find an issue anywhere.

This,
<li>Logout</li>
is not right.
The onclick attribute should reference a JavaScript handler. E.g. alert('peek-a-boo');. JSF/EL treats it as a plain vanilla string and expects that the logout() method returns some JavaScript code as String which should then be inlined in the HTML result. Imagine that the method actually returned alert('peek-a-boo');, then the final result (as you see in browser by rightclick, View Source) would be this:
<li>Logout</li>
However, in your particular case you're actually performing a logout and returning a string value of /index.xhtml?faces-redirect=true. So the generated HTML ends up being
<li>Logout</li>
Which is invalid JS code. But that's not the major problem: the user is been logged out without clicking the link!
You need a fullworthy JSF command component instead. E.g. <h:commandLink>.
<li><h:form><h:commandLink value="Logout" action="#{memberController.logout()}"/></h:form></li>
The method is this way only invoked when the link is actually clicked, which is exactly what you need.

Related

How to make a dynamically generated URL redirect to another tab?

I'm using PrimeFaces 6.2
Hi everyone. As mentionned in the title, I need to open a new tab when a user clicks on a link (which is dynamically generated). I tried 2 solutions for now, and none of them works entirely :
1st solution : attributes url and target in PrimeFaces component
Facelet :
<p:contextMenu id="menuMesure" for="treeVArboParents" nodeType="3">
<p:menuitem value="OPL" url="#{arboParObjView.sessionService.lienUrl()}" target="_blank"/>
</p:contextMenu>
View :
#Named(value="arboParObjView")
#ViewScoped
public class ArboParObjView implements Serializable
{
#Inject
SessionService sessionService;
private TreeNode selectedNode //changes everytime a node is selected - both right and left clicks work
...some code here...
public void genererLienBirt() //called everytime the selectedNode value is changed
{
String libelle="";
if (selectedNode != null)
{
//code to find the id of the associated to the selected node.
//I need the id because I want to pass it as a parameter of the link
//And this part of code works well
sessionService.setIdMesure(idMesure);
}
}
}
Session Service :
#Named(value="sessionService")
#SessionScoped
public class SessionService implements Serializable
{
private LienURL lienUrl = new LienURL();
public String lienUrl()
{
String lien = "";
if (idMesure != null)
{
lien = lienUrl.getUrl();
lien += idMesure.toString();
return lien;
}
return "";
}
}
Bean :
public class LienURL
{
private String url;
public LienURL()
{
this.url = "myLink&BirtParameter="; //The base link with a Birt parameter waiting for the idMesure to be passed.
}
}
This solution doesn't work. When the user click on the menu item of the context menu component, it's opening a new tab but the opened page is the same as the one the user just leaved. I think that's because the PF's attribute url loads the url once (and the first time, my url is null because the idMesure isn't filled yet), and it just ignores the good link I try to pass after idMesure is filled.
2nd solution : use the redirect of the FacesContext
Facelet :
<p:contextMenu id="menuMesure" for="treeVArboParents" nodeType="3">
<p:menuitem value="OPL" actionListener="#{arboParObjView.sessionService.lienUrl()}" />
</p:contextMenu>
Service :
#Named(value="sessionService")
#SessionScoped
public class SessionService implements Serializable
{
private LienURL lienUrl = new LienURL();
public void lienUrl() throws IOException
{
ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();
String url = lienUrl.getUrl()+idMesure.toString();
ec.redirect(url);
}
}
The bean and the view don't change. It's the same as in the 1st solution.
The second solution works better than the first one. It is opening the good page with the good url, but in the same tab as the page where the user was. Is there a way to use the FacesContext redirect, but in another tab, as the target="_blank" do (the target only works with the url attribute) ? Or is there a way to make the url attribute read other urls than the first passed (which is null) ?
Thanks, and excuse my english.
Please use target="_blank" in p:menuitem only in second solution and it should work.
Below is updated code
<p:contextMenu id="menuMesure" for="treeVArboParents" nodeType="3">
<p:menuitem value="OPL" actionListener="#{arboParObjView.sessionService.lienUrl()}" target="_blank" />
</p:contextMenu>
and
public void lienUrl() throws IOException
{
ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();
String url = lienUrl.getUrl()+idMesure.toString();
ec.redirect(url);
}
Thanks to all the contributors for their help. Solution below :
View :
#Named(value="arboParObjView")
#ViewScoped
public class ArboParObjView implements Serializable
{
#Inject
private TreePodeService treePodeService;
private TreeNode selectedNode;
private Integer idMesure;
private String lienOplBirt;
...
//redirect to the generated link (called by the UI)
public void redirectOpl()
{
try {
FacesContext.getCurrentInstance().getExternalContext.redirect(lienOplBirt);
} catch (IOException e) {
e.printStackTrace();
}
}
//generate the Birt Link
public void genererLienBirt()
{
String libelle = "";
if (selectedNode != null)
{
libelle = selectedNode.getData().toString();
VArboParObjectifsParents mesureSelected = treePodeService.getPodeArboObjParentDao().findByLibelle(libelle);
idMesure = mesureSelected.getIdRoot();
}
lienOplBirt = "https://theLinkToPass"+"&RP_idMesure="+this.idMesure;
}
...
//Execute the genererLienBirt() method everytime selectedNode's value changes
public void setSelectedTreeNode(TreeNode selectedNode) {
if (selectedNode != this.selectedNode)
{
this.selectedNode = selectedNode;
genererLienBirt();
}
this.selectedNode = selectedNode;
}
}
Facelet (UI)
<p:menuitem value="OPL" includeViewParams="true" action="#{arboParObjView.redirectOpl()}" ajax="false" />

Set instance variables of ManagedBean from database after login

I am starting to learn JSF and I am not sure if this is the right approach, but what I want to do is allow a user to login and validate from a database and then have a welcome message with the user's first name e.g. Welcome Bob. The database will have all the user info and login will consist of only the email and password.
Currently I have a ManagedBean with all the getters and setters plus a validation method, which calls a method in the DAO.
#Named(value = "custBean")
#SessionScoped
public class CustomerManagedBean implements Serializable {
/**
* Creates a new instance of CustomerManagedBean
*/
public CustomerManagedBean() {
}
private int custId;
private String firstname;
private String lastname;
private String email;
private String password;
private String address;
private String city;
private String state;
private int zip;
public int getCustId() {
return custId;
}
public void setCustid(int custId) {
this.custId = custId;
}
// More getters/setters here, not shown
public String validateEmailPassword() {
boolean valid = LoginDAO.validate(email, password);
if (valid) {
HttpSession session = (HttpSession) FacesContext.getCurrentInstance()
.getExternalContext().getSession(false);
session.setAttribute("email", email);
session.setAttribute("firstname", firstname); // I can't set this because user did not input this on login so how do I set it from database?
session.setAttribute("lastname", lastname); // and this
session.setAttribute("address", address); // and this
session.setAttribute("state", state); // and this
session.setAttribute("zip", zip); // and this
return "index";
} else {
FacesContext.getCurrentInstance().addMessage(
null,
new FacesMessage(FacesMessage.SEVERITY_WARN,
"Incorrect Email and Password",
"Please enter correct Email and Password"));
return "login";
}
}
Login.xhtml is very simple
<h:form>
<h3>Login</h3>
<h:outputText value="Username" />
<h:inputText id="username" value="#{custBean.email}"></h:inputText>
<h:message for="username"></h:message>
<br></br>
<h:outputText value="Password" />
<h:inputSecret id="password" value="#{custBean.password}"></h:inputSecret>
<h:message for="password"></h:message>
<br></br>
<h:commandButton action="#{custBean.validateEmailPassword}" value="Login"></h:commandButton>
</h:form>
Am I correct in that the bean gets initialized in the login.xhtml by user input on #{custBean.email} and #{custBean.password}? So can I set that bean's other variables after a database call? I hope I am making sense, but what I am able to do is after I login, I'm able to have #{custBean.email} displayed. It's just the firstname and the others I'm not able to show.
Here's my validate method in my DAO.
public static boolean validate(String email, String password) {
Connection con = null;
PreparedStatement ps = null;
try {
con = DataConnect.getConnection();
ps = con.prepareStatement("SELECT * FROM customer WHERE email = ? AND password = ?");
ps.setString(1, email);
ps.setString(2, password);
ResultSet rs = ps.executeQuery();
if (rs.next()) {
return true;
}
} catch (SQLException ex) {
System.out.println("Login error -->" + ex.getMessage());
return false;
} finally {
DataConnect.close(con);
}
return false;
}
UPDATE
I added an Customer entity from database and a CustomerController along with a generic AbstractFacade and CustomerFacade generated by Netbeans. My CustomerController is below. Is this a better way to interact with the DB?
#Named(value = "customerController")
#SessionScoped
public class CustomerController implements Serializable {
#EJB
CustomerFacade custFacade;
#Inject
CustomerManagedBean custBean;
public CustomerController() {
}
public String validateCustomer() {
Customer c = new Customer();
c = custFacade.getValidUser(custBean);
if (c != null) {
custBean.setEmail(c.getEmail());
custBean.setFirstname(c.getFirstname());
custBean.setLastname(c.getLastname());
custBean.setAddress(c.getAddress());
custBean.setCity(c.getCity());
custBean.setState(c.getState());
custBean.setZip(c.getZip());
}
}
The basic idea is, that you hold your model within your java class and link the model's values to your XHTML page via EL expressions.
So you can either get variables in your model filled from input elements of your page after submit or set them in your backend code and display them, both by having the value attribure reference your models member (which will go via getter/setter).
Your model gets initialized depending on the context you choose. As you went with SessionScope, it will be initialized on first usage and last as long as your HTTP session will last. In the meantime, you can for example read input after submitting your login data, query a database, fill your other members accordingly and display more data on this or another page, so in other words you have several roundtrips between your browser and your backend bean.
In your backing bean you don't have to fill any session attributes, you just fill your own model members after e.g. a database call and your index view (which I assume holds output elements for the data) will display the newly set model data when referred by EL expressions (just like you did with your input elements values).

How to get the values from multiple dynaforms?

I have been following this tutorial
http://www.primefaces.org/showcase-ext/sections/dynaform/basicUsage.jsf
I have been able to create tree Dynaform objects and send it to the page. But I am having a hard time obtaining the values that the user entered once they clicked submit. I want to be able to get these values in the backbean.
Here is submit button
<p:commandButton value="Submit" action="#{dynaFormController.submitForm}"
process="dynaForm" update=":mainForm:dynaFormGroup :mainForm:inputValues"
oncomplete="handleComplete(xhr, status, args)"/>
<p:commandButton type="reset" value="Reset" style="margin-left: 5px;"/>
I know the submit calls this function
<h:outputScript id="dynaFormScript" target="body">
/* <![CDATA[ */
function handleComplete(xhr, status, args) {
if(args && args.isValid) {
PF('inputValuesWidget').show();
} else {
PF('inputValuesWidget').hide();
}
}
/* ]]> */
</h:outputScript>
Then in the bean we have:
public String submitForm() {
FacesMessage.Severity sev = FacesContext.getCurrentInstance().getMaximumSeverity();
boolean hasErrors = (sev != null && (FacesMessage.SEVERITY_ERROR.compareTo(sev) >= 0));
RequestContext requestContext = RequestContext.getCurrentInstance();
requestContext.addCallbackParam("isValid", !hasErrors);
return null;
}
How would I be able to get either the fields values from the submitted form?
I have 3 dynaforms that I would like to submit them and be able to get the values in the back bean. Can anyone explain? I tried looking up some tutorials but I didn't find any explaining this.
Thanks.
It's the same as plain JSF.
You need a variable in your bean, its getters and setters.
Then, you compare it to the DynaFormControl.
#ManagedBean
#SessionScoped
public class DynaFormController implements Serializable {
private static final long serialVersionUID = 1L;
private DynaFormModel model;
private BookProperty bookProperty;
public String getBookProperty() {
return bookProperty;
}
public void setBookProperty(BookProperty bookProperty) {
this.bookProperty = bookProperty;
}
public String submitForm() {
//your code
List<DynaFormControl> controls = model.getControls();
for (DynaFormControl control : controls) {
if(control.getData() instanceof BookProperty) {
BookProperty bp = (BookProperty) c.getData();
//use the object
}
}
return null;
}
}

How to pause a Java "for" loop in order to expect a "p:commandbutton" click event ton be triggered in order to continue with the loop?

I have a list of books List<Book>, which I retrieve from my database.
Imagine that the Book class looks like this:
public class Book {
private String title;
private int pages;
// CONSTRUCTORS
public Book() {
}
public Book(String title, int pages) {
this.title = title;
this.pages = pages;
}
// GETTERS & SETTERS
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public int getPages() {
return pages;
}
public void setPages(int pages) {
this.pages = pages;
}
}
In my service bookService I write a method in which I loop through the List
List<Book> bookList;
and I check whether the number of pages is has a number or it is "0" zero. In case the value is "0" zero, I want to show a form/dialog asking the user to define the number of pages.
For that reason I have built a form in my XHTML, which looks like this:
<h:form id="BookPagesPromptForm">
<p:dialog header="Define number of pages"
widgetVar="BookPagesPromptDialogWidget" dynamic="true" modal="true"
resizable="false" width="300px" height="150px" position="center,top"
showHeader="true">
<div>Title : #{bookService.getTitle()}</div><br />
<div>Pages <p:inputText value="#{bookService.newPagesValue}" /></div><br />
<div><p:commandButton id="savePagesCommandButton" value="Save Pages Value" onclick="#{bookService.closeBookPagesForm()}" /></div>
</p:dialog>
</h:form>
In my service I have the following method which I use to loop through my booList:
public class BookService {
// ...
// ... Code ...
// ...
private List<Book> bookList;
private IBookDAO bookDao;
// ...
// ... More Code ...
// ...
public void updateRecordsWithZeroPages() {
for (Book bk : bookList) {
if (bk.getPages() == 0) {
RequestContext context = RequestContext.getCurrentInstance();
context.execute("PF('BookPagesPromptDialogWidget').show();");
// Here I need to wait for user's input and as soon as the user
// clicks
// the "savePagesCommandButton" I want to update the record in
// my
// database.
bookDao.update(bk);
}
}
}
public void closeBookPagesForm() {
RequestContext context = RequestContext.getCurrentInstance();
context.execute("PF('BookPagesPromptDialogWidget').hide();");
}
}
In my flow book-flow.xml I have defined my service (partial code):
<view-state id="bookList">
<var name="bookService" class="com.stavros.BookService" />
</view-state>
The proble is that in my loop it does not stop when in the first record which has "0" zero pages, but it goes through all records and stops only when the loop is finished. As a consequence the form appears and asks the user about the last value from the loop.
In case you know how to make a "pause" in the loop and continue the loop only when the user click on the the "savePagesCommandButton" on the UI, please let me know.

Dynamic message display

Set<String> testPopUp = new HashSet<String>()
//getter
public Set<String> gettestPopUp() {
return testPopUp;
}
//setter
public void settestPopUp(Set<String> testPopUp ) {
this.testPopUp = testPopUp ;
}
<h:alertPopup id="popupMessagesId" title="Error" message="#{testBean.testPopUp }" icon="info">
<h:a4jButton id="popUp" value="Ok" onclick="javascript:Richfaces.hideModalPanel('popupMessagesId')"/>
</h:alertPopup>
Now my Java Code :
TestBean.java :
public void test(){
//validation methods
testPopUp.add("This is an error");
AjaxCommandBean.showPopupOnComplete("popupMessagesId");
}
Now, the popup is showing NULL value, as getter : gettestPopUp() is called when the page is loaded, and at that time testPopUp will be NULL. testPopUp is populated only after test() method is executed. So, when I load the page second time I am getting the value.
Please let me know how can I get the value first time in pop up.
I am using JSF2.0

Resources