I am new to JSF (Have Fair knowledge in Struts) have tried some examples of JSF from internet and i have some doubts regarding the flow.
The example is some thing like below
Login.xhtml
<div>
<h:panelGrid columns="2">
<h:outputText value="Name"></h:outputText>
<h:inputText value="#{loginBean.name}"></h:inputText>
<h:outputText value="Password"></h:outputText>
<h:inputSecret value="#{loginBean.password}"></h:inputSecret>
<h:commandButton value="Login" action="login"></h:commandButton>
</h:panelGrid>
</div>
faces-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<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">
<managed-bean>
<managed-bean-name>loginBean</managed-bean-name>
<managed-bean-class>com.tutorial.LoginBean</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
</managed-bean>
<navigation-rule>
<display-name>template/Login.xhtml</display-name>
<from-view-id>/template/Login.xhtml</from-view-id>
<navigation-case>
<from-outcome>login</from-outcome>
<to-view-id>/template/Welcome.xhtml</to-view-id>
</navigation-case>
</navigation-rule>
</faces-config>
LoginBean:
package com.tutorial;
public class LoginBean
{
private String name;
private String password;
public String getName ()
{
return name;
}
public void setName (final String name)
{
this.name = name;
}
public String getPassword ()
{
return password;
}
public void setPassword (final String password)
{
this.password = password;
}
}
Flow is that in login page we will give username and password and it will go to welcome page and will display the name
Listing out my queries below PLEASE help me in to figuer these things and get a better understanding of JSF
In struts we will have code something like below
<action path="/LoginAction" type="com.app.action.LoginAction" name="LoginForm">
so for this "LoginAction" it will go to the LoginAction class and the bean or DTO will be LoginForm. in the above faces-config.xml the login.jsp is not linked to any bean class.
In struts we have execute method. do we have any default method in JSF like that ?
In JSF if we have a requirement like "when we click on a button in JSP the control should go to a java class and based on authentication in java we will go the success or failure page". what should be the configaration in faces-config.xml? in <navigation> tag ther is only tag with <to-view> (which means a JSP to my understanding) what should we do if the control should go to java class.
in the above faces-config.xml the login.jsp is not linked to any bean class.
I'm not sure why that's necessary. JSF is component based, not request based.
In struts we have execute method. do we have any default method in JSF like that ?
Nope, you've the full freedom to bind the action attribute of a command component to any arbitrary method of the backing bean.
E.g.
<h:commandButton value="Login" action="#{loginBean.submit}" />
with
public String submit() {
// Do your thing here.
// ...
return "login";
}
Note that the method name is fully free to your choice. You can also use #{loginBean.login} with a public String login() method. I'm not sure why you would be restricted to a single predefined action name. JSF is component based, not request based.
"when we click on a button in JSP the control should go to a java class and based on authentication in java we will go the success or failure page". what should be the configaration in faces-config.xml? in tag ther is only tag with (which means a JSP to my understanding) what should we do if the control should go to java class.
Create another <navigation-case>
<navigation-case>
<from-outcome>error</from-outcome>
<to-view-id>/template/Error.xhtml</to-view-id>
</navigation-case>
and handle accordingly in the action method:
public String submit() {
// Do your thing here.
// ...
if (success) {
return "login";
} else {
return "error";
}
}
Noted should be that navigation cases are soo JSF 1.x. On JSF 2.x they are not necessary anymore. Just get rid of the whole <navigation-rule> block in faces-config.xml and make use of JSF implicit navigation feature.
if (success) {
return "/template/Welcome";
} else {
return "/template/Error";
}
See also:
Communication in JSF 2.0
Related
I have my RewriteConfiguration class
#RewriteConfiguration
public class ApplicationConfigurationProvider extends HttpConfigurationProvider {
#Override
public int priority() {
return 0;
}
#Override
/**
* map URL with resource
*/
public Configuration getConfiguration(ServletContext context) {
return ConfigurationBuilder.begin()
.addRule(Join.path("/page1/{param}").to("/view/page1.jsf?product={param}"))
.addRule(Join.path("/page2/{param}").to("/view/page2.jsf?product={param}"))
;
}
}
My managedBean
#Named("myBean")
#javax.enterprise.context.RequestScoped
public class PageManagedBean {
...
public String success(){
return "success";
}
...
}
In my page1.xhtml my submit button code is
<h:commandButton action="#{myBean.success()}" id="nextButton" value="NEXT" >
<f:param name="product" value="#{param.product}"></f:param>
</h:commandButton>
And my navigation rule in faces-config.xml is
<navigation-rule>
<from-view-id>/view/page1.xhtml</from-view-id>
<navigation-case>
<from-outcome>success</from-outcome>
<to-view-id>/view/page2.jsf</to-view-id>
</navigation-case>
<navigation-case>
<from-outcome>error</from-outcome>
<to-view-id>/error-500</to-view-id>
</navigation-case>
</navigation-rule>
The problem is :
When I am click on submit button from page1 , in the browser URL it is showing /page1/xyz even-though the page is redirected to page2
The requirement is to change the url to /page2/xyz.
I added <redirect/> in faces-config.xml , but in URL it is showing the full path of the page2 (/view/page2.jsf) .
How can I redirect to next page with change in URL ?
Your Join looks strange. You should not use query parameters in the to() part. Rewrite automatically converts all path parameters to query parameters.
So instead of:
.addRule(Join.path("/page1/{param}").to("/view/page1.jsf?product={param}"))
You should try:
.addRule(Join.path("/page1/{product}").to("/view/page1.jsf"))
Now your navigation should work. But please include the redirect element. That’s required for the URL to change.
I'm trying to set up a voting app that will display whether the user is able to vote or not using an if statement in my bean class but this Unable to find matching navigation case with from-view-id '/home.xhtml' for action '#{user.checkAge(user.age)}' with outcome 'Invalid User, Please Try Again!!!'. Im not very understanding of Java Server Faces yet and ive tried messing around with the config files and googling the error but i cant fix it. Can anyone help me please.
Here is my code:
Home.xhtml
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html">
<head>
<title>Results Page</title>
</head>
<body>
<h:form>
Name: <h:outputText id="outTxt" value="#{user.name}"/><br></br>
Age: <h:outputText id="outTxt2" value="#{user.age}"/><br></br>
<h:commandButton id="cmdBtn" value="Check" action="#{user.checkAge(user.age)}"/>
</h:form>
</body>
</html>
index.xhtml
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html">
<h:head>
<title>Home Page</title>
</h:head>
<h:body>
<h:body>
<h:form>
Name: <h:inputText id="inTxt" value="#{user.name}"/><br></br>
Age: <h:inputText id="inTxt2" value="#{user.age}"/><br></br>
<h:commandButton id="cmdBtn" value="Check" action="home"/>
</h:form>
</h:body>
</h:body>
</html>
User.java
package MyPackage;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
#ManagedBean
#SessionScoped
public class User
{
private String name;
private int age;
private String msg;
public String getMsg()
{
return msg;
}
public void setMsg(String msg)
{
this.msg = msg;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public int getAge()
{
return age;
}
public void setAge(int age)
{
this.age = age;
}
public String checkAge(int checkAgeVoter)
{
if(checkAgeVoter >= 18)
{
msg = "Valid User, Access Granted!!!";
}
else
{
msg = "Invalid User, Please Try Again!!!";
}
return msg;
}
}
User.checkAge() is used as JSF action. Therefore its return value is interpreded as "outcome" which is fed into JSF navigation logic. JSF tries to look up a navigation rule for the current view (/home.xhtml) and your "outcome" to find out which page to display next. It can not find any.
You might want to return null from checkAge (or make the method void) causing JSF navigation to display the current page again. However, I did not see any place where User.msg would be displayed. So you might want to navigate to another page which does that and return the proper outcome for a navigation rule. (Note: Your user inputs data in index.html so you might want to reference your input validating action there and return "/home.xhtml".) For more details on navigation in modern JSF, see Faces Navigation not really working in JSF2
Note that there is a mechanism for displaying messages in JSF based on the tags h:messages (and h:message) and FacesContext.getCurrentInstance().addMessage() (and validation). For an example, see http://www.javabeat.net/2008/04/display-error-messages-in-jsf-hmessages-part-1-2/
You might try:
In home.xhtml: After <f:form> add Msg: <h:outputText value="#{user.msg}"/><br/>.
In index.xhtml: Replace action="home" with action="#{user.checkAge(user.age)}".
In User.java: In checkAge() replace return msg; with return "home"; (or "/home.xhtml").
Assuming index.html is your starting page, this checks entered data and navigates to home page (assuming action="home" already worked).
This is not the best way to do it, but just to well inderstand how the navigation goes on by the famous configuration file faces-config :
First, you can omit the msg attribute, and modify the business method as :
public String checkAge(int checkAgeVoter) {
if (checkAgeVoter >= 18) return "granted";
else return "denied";
}
With this faces-config :
<?xml version="1.0" encoding="UTF-8"?>
<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">
<navigation-rule>
<from-view-id>/index</from-view-id>
<navigation-case>
<from-outcome>home</from-outcome>
<to-view-id>/home.xhtml</to-view-id>
<redirect/>
</navigation-case>
</navigation-rule>
<navigation-rule>
<from-view-id>/home.xhtml</from-view-id>
<navigation-case>
<from-outcome>granted</from-outcome>
<to-view-id>/votepage.xhtml</to-view-id>
<redirect/>
</navigation-case>
<navigation-case>
<from-outcome>denied</from-outcome>
<to-view-id>/errorpage.xhtml</to-view-id>
<redirect/>
</navigation-case>
</navigation-rule>
And adding 2 test pages: votepage.xhtml in case of success, and errorpage.xhtml else.
In my app, user should be able to switch the locale (the language used to render text on pages). Tons of tutorials are using FacesContext.getCurrentInstance().getViewRoot().setLocale(). For example: http://www.mkyong.com/jsf2/jsf-2-internationalization-example/. But, that simply doesn't work in JSF 2.0 (it did work in 1.2). The language never switches. No errors or anything. The same code worked fine in JSF 1.2.
What is the correct and definitive approach? I have cobbled together a solution, but not sure if this is the correct one. This works fine. The language switches after user clicks on English or French. Here is code snippet to give you some idea.
#ManagedBean(name = "switcher")
#SessionScoped
public class LanguageSwitcher {
Locale locale = FacesContext.getCurrentInstance().getViewRoot().getLocale();
public String switchLocale(String lang) {
locale = new Locale(lang);
return FacesContext.getCurrentInstance().getViewRoot().getViewId() +
"?faces-redirect=true";
}
//getLocale() etc. omitted for brevity
}
The XHTML:
<f:view locale="#{switcher.locale}">
<h:outputText value="#{msg.greeting}" />
<h:commandLink value="English" action="#{switcher.switchLocale('en')}" />
<h:commandLink value="French" action="#{switcher.switchLocale('fr')}" />
</f:view>
Just to give you more info, here is the config file.
<application>
<locale-config>
<supported-locale>en</supported-locale>
<supported-locale>fr</supported-locale>
</locale-config>
<resource-bundle>
<base-name>com.resources.Messages</base-name>
<var>msg</var>
</resource-bundle>
</application>
Once again, this works. But, I haven't changed the locale of JSF itself by calling any API in any way. This gives me somewhat of a creepy feeling. Is this the correct way to change user's locale?
OK, at the risk of answering my own question, I will like to summarize all the different approaches that I have found.
The basic approach is what I am already doing. That is, have a managed bean in session scope that returns the Locale of the user. This locale needs to be used in every XHTML using <f:view locale="...">. I learned this technique from a post by BalusC, so thanks are due there.
Now, the concern is the use of the f:view element. This needs to be repeated in every page, a potential source of defect if omitted by mistake. I have found a couple of ways of solving this problem.
Approach #1: Create a Facelet template and add the f:view element there. Individual template user pages don't have to worry about adding this element.
Approach #2 uses a phase listener. #meriton has posted the solution here. Thank you for that.
Approach #3 uses a custom view handler that extends MultiViewHandler and returns user's locale from the calculateLocale() method. This is described in the book Beginning JSF 2 APIs and JBoss Seam By: Kent Ka Iok Tong. Here is a slightly altered example from the book:
public class MyViewHandler extends MultiViewHandler {
public Locale calculateLocale(FacesContext context) {
HttpSession session = (HttpSession) context.getExternalContext()
.getSession(false);
if (session != null) {
//Return the locale saved by the managed bean earlier
Locale locale = (Locale) session.getAttribute("locale");
if (locale != null) {
return locale;
}
}
return super.calculateLocale(context);
}
}
Then register it in faces config.
<application>
<view-handler>com.package.MyViewHandler</view-handler>
<!-- Other stuff ... -->
</application>
This is somewhat more elegant than the phase listener. Unfortunately, MultiViewHandler is an internal non-API class from the com.sun.faces.application.view package. That incurs some risk going forward.
With either approach #2 or #3, there is no need for the f:view element in the pages.
One can use custom view handler that extends javax.faces.application.ViewHandlerWrapper and returns user's locale from the calculateLocale() method.
This is definitely better than extending MultiViewHandler from the proprietary SUN package com.sun.faces.application.view, no matter what is described in the book Beginning JSF 2 APIs mentioned in your suggestion. Apart from that, your original approach is absolutely OK:
public class MyViewHandler extends ViewHandlerWrapper {
public Locale calculateLocale(FacesContext context) {
HttpSession session = (HttpSession) context.getExternalContext()
.getSession(false);
if (session != null) {
//Return the locale saved by the managed bean earlier
Locale locale = (Locale) session.getAttribute("locale");
if (locale != null) {
return locale;
}
}
return super.calculateLocale(context);
}
}
Then register it in faces config.
<application>
<view-handler>com.package.MyViewHandler</view-handler>
<!-- Other stuff ... -->
</application>
I had a related problem recently. In my case, the JSF implementation forgot the view locale set by UIViewRoot.setLocale() after navigating to a different view. I rather consider this a bug in the JSF impl, but I didn't have time to make sure.
I didn't particularly like the <f:view> approach, as that tag has been obsoleted by facelets - except for keeping the locale, it seems. This made my leary of including it in a Facelets template. I therefore wrote the following PhaseListener:
/**
* PhaseListener that keeps the current view locale in the session while no request is being processed, to work around
* bugs where JSF forgets the changed locale.
*/
public class SaveViewLocaleToSessionPhaseListener implements PhaseListener {
private static final String key = "locale";
#Override
public PhaseId getPhaseId() {
return PhaseId.ANY_PHASE;
}
#Override
public void beforePhase(PhaseEvent event) {
// do nothing
}
#Override
public void afterPhase(PhaseEvent event) {
PhaseId currentPhase = event.getPhaseId();
if (currentPhase == PhaseId.RESTORE_VIEW) {
viewRoot().setLocale((Locale) sessionMap().get(key));
} else if (currentPhase == PhaseId.RENDER_RESPONSE) {
sessionMap().put(key, viewRoot().getLocale());
}
}
private Map<String, Object> sessionMap() {
return FacesContext.getCurrentInstance().getExternalContext().getSessionMap();
}
private UIViewRoot viewRoot() {
return FacesContext.getCurrentInstance().getViewRoot();
}
}
However, I can not offer any solid evidence that this is really better than simply using <f:view>.
But, I haven't changed the locale of JSF itself in any way.
Sure you did: The <f:view> tag reads the locale from the value expression, and passes it to UIViewRoot.setLocale().
Simple question from a beginner at JSF:
I have very simple JSF form:
<h:form>
<p>#{messages.loginTextfieldUsername}</p>
<h:inputText value="#{userServiceImpl.user.name}" />
<p>#{messages.loginTextfieldPassword}</p>
<h:inputSecret value="#{userServiceImpl.user.password}" />
<h:commandButton value="#{messages.loginButtonLogin}" action="#{userServiceImpl.authenticateUser}" />
</h:form>
The userServiceImpl class is:
#Named
#RequestScoped
public class UserServiceImpl implements UserService {
private UserSession userSession;
private User user;
#Inject
public UserServiceImpl(UserSession userSession) {
this.userSession = userSession;
}
#PostConstruct
public void prepareService() {
user = new User();
}
#Override
public View authenticateUser() {
userSession.setLoggedUser(user);
return View.MAIN;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
My goal is pretty simple: when the user hits the login button, I want to authenticate the user.
The problem is:
When the authenticate method is called, the User attributes are null. I debugged the application and the getUser method is called and the values are properly set, but at some point (which I did not find [yet]) before the authenticateUser is called the User attributes are set to null...
I'm aware that this is a pretty basic question... but are you able to point out where my mistake is?
Thanks.
Based on your previous question, you seem to have experimented with <managed-bean-scope> of none in faces-config.xml for some reason. The problem symptoms matches exactly when using #ManagedBean #NoneScoped. You seem to have configured this bean in faces-config.xml as well on a none scope which totally explains this problem. With the none scope, a brand new bean instance is been created everytime when #{userServiceImpl} is been evaluated in EL. Your form submit has thus effectively created 3 beans: one where the user name is set, another one where user password is set and another one where action is invoked.
You need to remove the managed bean configuration from faces-config.xml. You should not use it when you intend to use #Inject (or #ManagedBean). The faces-config.xml way of configuring beans is a leftover from old JSF 1.x ages when annotations weren't available. As of JSF 2.x it would only override any bean management annotations.
Ok, with all the answers to this question I'm still not able to handle my problem.
I have the following constellation:
In a JSF (1.1) webapp I have a request scoped bean beanof class Bean. When the user quickly clicks a commandButton multiple times to redirect him to the insult.xhtml page the doSomethingThatTakesALittleLongerAndShouldOnlyBeDoneOncemethod may get invoked multiple times (on Tomcat 6). How can I prevent this?
...
public Bean() {
HttpSession session = ((HttpSession) FacesContext.getCurrentInstance()
.getExternalContext().getSession(false));
if(session != null && session.getAttribute("done") != null) {
doSomethingThatTakesALittleLongerAndShouldOnlyBeDoneOnce();
session.setAttribute("done", "done");
}
}
public void doSomethingThatTakesALittleLongerAndShouldOnlyBeDoneOnce() {
this.bossInsult = generateBossInsult();
}
insult.xhtml:
<ui:composition xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core">
<html>
<body>
#{bean.bossInsult}
</body>
</html>
</ui:composition>
Make the bean session scoped and annotate the method with #PostConstruct. If you insist in keeping it request scoped, split that part out into a session scoped bean and make it a managed property of the request scoped bean using #ManagedProperty.
#ManagedBean
#RequestScoped
public class Bean {
#ManagedProperty(value="#{insultBean}")
private InsultBean insultBean;
}
and
#ManagedBean
#SessionScoped
public class InsultBean {
#PostConstruct
public void init() {
this.bossInsult = generateBossInsult();
}
}
Then JSF will take care that it's created and called only once during the session.
Update: sorry, you're using JSF 1.x. If it's 1.2, then the following achieves the same:
public class Bean {
private InsultBean insultBean;
}
and
public class InsultBean {
#PostConstruct
public void init() {
this.bossInsult = generateBossInsult();
}
}
and
<managed-bean>
<managed-bean-name>insultBean</managed-bean-name>
<managed-bean-class>com.example.InsultBean</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
</managed-bean>
<managed-bean>
<managed-bean-name>bean</managed-bean-name>
<managed-bean-class>com.example.Bean</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
<managed-property>
<property-name>insultBean</property-name>
<value>#{insultBean}</value>
</managed-property>
</managed-bean>
Make the button disabled with javascript once it's clicked.