I’m creating a Bill-Pay application that
- Display a login screen
- After successful login, displays list of all available Payee in system
- User can select relevant payees from the list / ignore them\
- Add selected payees in their profile
My first facelet (Login.xhtml) displays a web page with two
input fields, Login and Password. I’m using JSF standards validation mechanism
to validate these fields, appropriate error message will be displayed if any of
these fields fail validation.
System will also display appropriate error message if
authentication fails. Upon successful login, next screen will display list of
available Payee in the application.
Login.xhtml:
<html lang="en"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<h:head>
<h:outputStylesheet libary="css"
name="default.css" />
<title>Login</title>
</h:head>
<h:body>
<h:form>
<h:graphicImage library="images"
name="wave.med.gif"
alt="Duke waving his hand" />
<p>
<h:outputText value="#{bundle.userNameLabel}"/>
<h:inputText id="userName"
title="Enter user id"
value="#{userAuthenticationBean.userName}">
<f:validateRequired/>
<f:validateLength minimum="#{userAuthenticationBean.minLengthUserName}"
maximum="#{userAuthenticationBean.maxLengthUserName}"/>
</h:inputText>
</p>
<h:message showSummary="true"
showDetails="false"
style="color: #d20005;
font-family: 'New Century
Schoolbook', serif;
font-style: oblique;
text-decoration: overline"
id="errors1" for="userName"
/>
<p>
<h:outputText value="#{bundle.passwordLabel}"/>
<h:inputSecret id="password"
titl="Enter password"
value="#{userAuthenticationBean.password}">
<f:validateRequired/>
</h:inputSecret>
</p>
<h:message showSummary="true"
showDetails="false"
style="color: #d20005;
font-family: 'New Century
Schoolbook', serif;
font-style: oblique;
text-decoration: overline"
id="errors2" for="password"
/>
<p>
<h:commandButton id="submit"
value="Submit" action="showPayeeList"
/>
</p>
</h:form>
</h:body>
</html>
At this point, I created a Managed Bean
(UserAuthenticationBean.java) to perform authentication.
UserAuthenticationBean.java
package billpay.web.consumer;
import java.io.Serializable;
import java.util.Random;
import
javax.enterprise.context.SessionScoped;
import javax.inject.Named;
@Named
@SessionScoped
public class UserAuthenticationBean
implements Serializable{
private
static final long serialVersionUID = 5443351151396868724L;
public
int MinLengthUserName=4;
public
int MaxLengthUserName=10;
private
String userName;
private String password;
public String getPassword()
{
return password;
}
public void setPassword(String
password) {
this.password = password;
}
public
String getUserName() {
return
userName;
}
public
void setUserName(String userName) {
this.userName
= userName;
}
}
At this point, when I compiled my
application, everything looked good, however deployment failed with below
error:
org.jboss.weld.exceptions.DefinitionException: WELD-000075 Normal
scoped managed bean implementation class has a public field: [EnhancedAnnotatedFieldImpl] public
billpay.web.consumer.UserAuthenticationBean.minLengthUserName
at
org.jboss.weld.bean.ManagedBean.checkBeanImplementation(ManagedBean.java:225)
The reason for this failure was
‘public’ fields in my Bean. After some googling, I looked at this post.
Since I’m using JSF 2, I decided
to use omnifaces to import my constants from Bean class. It was quite easy to
integrate omnifaces, just drop a jar in WEB-INF/lib, and declare the tag on
your facelet.
Read more about Omnifaces here
See changes in my facelet to
integrate the omnifaces tags below.
<html lang="en"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:o="http://omnifaces.org/ui"
xmlns:of="http://omnifaces.org/functions">
<h:body>
<o:importConstants
type="billpay.web.consumer.UserAuthenticationBean"/>
<h:inputText id="userName"
title="Enter user id"
value="#{userAuthenticationBean.userName}">
<f:validateRequired/>
<f:validateLength minimum="#{UserAuthenticationBean.MinLengthUserName}"
maximum="#{UserAuthenticationBean.MaxLengthUserName}"/>
Also I had to make some changes
in my Bean class to declare my fields as constants, by making them ‘static’ and
‘final’.
public static final int MinLengthUserName=4;
public static final int MaxLengthUserName=10;
I deployed my application successfully
in Tomcat, everything looked good and typing this URL (http://localhost:8080/billpay/faces/login.xhtml)
in address bar displayed my login facelet.
However I also noticed that my validation was not working as
expected. If I click on Submit without entering any text in Login and Password
field, JSF was not throwing any validation error in spite of <f:validateRequired/>
tag.
After some reading and scanning
APIs, I came to know that we need to add a context parameters that will
instruct JSF to perform validation for NULL/EMPTY fields.
<context-param>
<param-name>javax.faces.VALIDATE_EMPTY_FIELDS</param-name>
<param-value>true</param-value>
</context-param>
See javax.faces.validator.Validator
Javadoc for more information.
My next task was to set up
authentication. J2EE tutorial discusses
several approaches
for implementing this functionality.
Since UserAuthenticationBean already had login and password values, I added
a new method to perform login operation:
public String login(){
if(this.userName.equals("Vicky") && this.password.equals("USA")){
System.out.println("You're good
to go");
return "showPayeeList";
}else{
FacesContext
context = FacesContext.getCurrentInstance();
context.addMessage(null, new FacesMessage("Login
failed."));
return null;
}
}
I also had to make some changes in
facelet to invoke this operation for login operation.
<h:commandButton id="submit"
value="Submit" action="#{userAuthenticationBean.login}" />
If I enter correct credentials, system
will display ‘showPayeeList’ facelt that diplays all available payees in the
system, however if authentication fails, system displays Login page again with
error message ‘Login failed’.
In my login facelet I didn’t add
any <h:messge/> tag to display the error message, however it does that automatically
because we’ve set PROJECT_STAGE to ‘Development’.
<context-param>
<param-name>javax.faces.PROJECT_STAGE</param-name>
<param-value>Development</param-value>
</context-param>
WOW!!
Now my next task is to display list of all available Payee .
In our application, we usually
get list of Payees from database, however to simulate that I created a new RequestScoe
ManagedBean (PayeeBean) and an Entity (PayeeEntity) to retrieve list of Payees.
For this simple application, there was no need of an Entity, however I wanted
to create a layer to separate Presentation and Data layers.
@Named
@RequestScoped
public class PayeeBean implements Serializable{
private static final long serialVersionUID =
5443352251396868724L;
/**
* Returns all Payees that exists in Bill
Payment system. Ideally this method
* would refer some EJBs or some other data sourcesin
actual application.
* @return List<PayeeVO>
* @throws Exception
*/
public List<PayeeVO>
getPayeeList(){
return PayeeEntity.getPayeeList();
}
}
public class PayeeEntity {
public static List<PayeeVO>
getPayeeList(){
List<PayeeVO>
payeeList = new ArrayList<PayeeVO>();
payeeList.add(new PayeeVO(1,"Comcast"));
payeeList.add(new PayeeVO(2,"Sawnee"));
payeeList.add(new PayeeVO(3,"Macy's"));
payeeList.add(new PayeeVO(4,"Walgreens"));
return payeeList;
}
}
Once I’ve list of Payees, my next
task was to display them on a web page that will allow user to select
appropriate payees.
Also this page will have option
to ‘Add’ selected Payee in consumer’s profile, or clear the selection.
Since all my Payees were stored
in a java.util.List, <h:dataTable> was obvious choice, however I had to
display a checkbox for every Payee that will allow users to make selection.
Depending upon your requirements, we could also use <ui:repeat> or
<c:forEach> to display these values, however I stick with JSF standard
components to explore their features.
showPayeeList.xhtml
<html lang="en"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<h:head>
<h:outputStylesheet library="css"
name="default.css" />
<title>Payee List</title>
</h:head>
<h:body>
<h:form>
<h:dataTable value="#{payeeBean.payeeList}"
var="payee">
<f:facet name="caption">List of Payee</f:facet>
<h:column>
<f:facet name="header">Payee's Name</f:facet>
#{payee.name}
</h:column>
<h:column>
<f:facet name="header">Add</f:facet>
<h:selectBooleanCheckbox
value="#{consumerBean.selectedPayee[payee.id]}"
/>
</h:column>
</h:dataTable>
<h:commandButton value="Submit"
action="#{consumerBean.showSelectedPayee}" />
<h:commandButton value="Clear"
action="#{consumerBean.clearSelectedPayee}"
/>
</h:form>
</h:body>
</html>
I also created a new bean (ConsumerBean)
to store these selected values. Also this bean will be stored in Session scope because
it holds consumer specific values.
ConsumerBean.java
@Named
@SessionScoped
public class ConsumerBean implements Serializable{
private static final long serialVersionUID =
5443351151876868724L;
private Map<Integer,
Boolean> selectedPayee = new HashMap<Integer, Boolean>();
private List<PayeeVO> selectedPayeeList = new
ArrayList<PayeeVO>();
private List<PayeeVO> payeeList = PayeeEntity.getPayeeList();
public Map<Integer,
Boolean> getSelectedPayee(){
return selectedPayee;
}
public String
showSelectedPayee(){
for(PayeeVO payee: payeeList){
if(selectedPayee.get(payee.getId()))
selectedPayeeList.add(payee);
}
selectedPayee.clear();
return "showSelectedPayee";
}
public String
clearSelectedPayee(){
selectedPayeeList.clear();
return null;
}
public List<PayeeVO>
getSelectedPayeeList(){
return selectedPayeeList;
}
}
After successful login, system displays
all available Payees (below)
I selected ‘Comcast’ and ‘Sawnee’,
clicked on ‘Add’, and here is my next screen
Details of other config files below:
faces-config.xml
<faces-config 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-facesconfig_2_0.xsd"
version="2.0">
<application>
<resource-bundle>
<base-name>billpay.web.messages.Messages</base-name>
<var>bundle</var>
</resource-bundle>
<locale-config>
<default-locale>en</default-locale>
<supported-locale>es</supported-locale>
</locale-config>
</application>
</faces-config>
Web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>/faces/*</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.faces</welcome-file>
</welcome-file-list>
<context-param>
<param-name>javax.faces.PROJECT_STAGE</param-name>
<param-value>Development</param-value>
</context-param>
<context-param>
<param-name>javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL</param-name>
<param-value>true</param-value>
</context-param>
<context-param>
<param-name>javax.faces.VALIDATE_EMPTY_FIELDS</param-name>
<param-value>true</param-value>
</context-param>
<listener>
<listener-class>org.jboss.weld.environment.servlet.Listener</listener-class>
</listener>
</web-app>
Messages_en.properties
userNameLabel=Login:
passwordLabel=Password:
Workspace
Overall I’m amazed with
simplicity offered by JSF. It took me approximately 2 hours to complete this simple
application. Thanks for reading.
No comments:
Post a Comment