Conditional navigation not working properly in JSF - java

I am developing a website with JSF 2.0, hibernate and primefaces.
I'm implementing an admin panel where the website admin can put the whole application on "maintenance mode". This is done by changing a flag on an application scoped bean (called on purpose AppConfigurationBean).
If the flag is active the user is necessarily returned to a page, called maintenance.xhtml, by a conditional navigation rule declared in faces-config.xml.
The user can bypass the maintenance page by inserting a special bypass key which can be given seldom by the admin. If he does so, a flag is activated on a special session scoped bean, which holds information about the user and is called CurrentClientSessionBean.
The navigation rule works if the value returned from a special method on the CurrentClientSessionBean, which take in counts the maintenance mode flag on the application bean and the "has bypass key" flag on himself.
Here is the navigation rule on faces-config.xml:
<navigation-rule>
<description>System</description>
<from-view-id>*</from-view-id>
<navigation-case>
<display-name>Maintenance Mode</display-name>
<from-outcome>*</from-outcome>
<if>#{currentClientSessionBean.toReturnToMaintenance}</if>
<to-view-id>/maintenance.xhtml</to-view-id>
<redirect />
</navigation-case>
</navigation-rule>
Here is the maintenance.xhtml page:
<ui:composition id="maintenance" xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:p="http://primefaces.org/ui"
xmlns:plm="http://java.sun.com/jsf/composite/plmCustomComponents">
<f:verbatim><!DOCTYPE html></f:verbatim>
<html>
<h:head>
<title>#{msg.applicationTitle}</title>
<meta charset="UTF-8"></meta>
<meta http-equiv="content-type" content="text/html; charset=utf-8"></meta>
<h:outputStylesheet library="default" name="css/normalize.css" />
<h:outputStylesheet library="default" name="css/icons.css" />
<h:outputStylesheet library="default" name="css/main.css" />
</h:head>
<f:metadata>
<ui:include src="metadata/browserViewParams.xhtml" />
</f:metadata>
<h:body>
<div id="legalPart">
<ui:insert name="legalPart">
<ui:include src="/structure/legalPart.xhtml" />
</ui:insert>
</div>
<p:panel>
<h1>
<h:outputText value="#{msg.maintenancePageTitle}" />
</h1>
<p>
<h:outputText value="#{msg.maintenancePageBodyMsg}" />
</p>
<h:form>
<p:outputLabel for="bypassKeyInput"
value="#{msg.maintenancePageInsertBypassKeyLabel}" />
<br />
<p:inputText id="bypassKeyInput"
value="#{maintenanceBacking.keyBuffer}" required="true" />
<br />
<p:message for="bypassKeyInput" />
<p:commandButton value="#{msg.checkPassword}"
action="#{maintenanceBacking.checkBypassKey}" ajax="false" />
</h:form>
</p:panel>
<ui:debug />
</h:body>
</html>
</ui:composition>
The maintenance backing is really simple, it simply checks if the bypasskey matches the true one and does a simple navigation to the home page if true.
Now the CurrentClientSessionBean method for the conditional navigation is this one:
/**
* Special getter that determines if user should return to maintenance
*
* #return
*/
public boolean isToReturnToMaintenance(){
try{
AppConfigurationBean appConfigurationBean = JSFAppUtils.findApplicationBean("appConfigurationBean");
boolean result = ((!this.hasBypassKey) && appConfigurationBean.isMaintenanceModeOn());
return result;
}catch(NotFoundException e){
logger.error("NotFoundException thrown in isToReturnToMaintenance method", e);
throw new RuntimeException("NotFoundException thrown in isToReturnToMaintenance method", e);
}
}
Here is the main problem: for some odd reason, when the maintenance mode is active the user is forced to return to maintenance page even if he puts the right bypass key in the input.
Furthermore: There must be something really wrong within my application, because i tried to comment the conditional navigation rule, which means that maintenance page should be unreachable and changes to the maintenance mode should have no effect on the application, but still the maintenance mode system went on working with the same attitude I described.

It seems that I added a redirect on the homepage which was connected to the maintenanceModeOn boolean flag, and didn't take in count the hasBypassKey flag.
The rule was deeply nested in some part of the code which I wrote long long time ago and I forgot about.
<h:panelGroup rendered="#{appConfigurationBean.maintenanceModeOn}">
<script>
window.location = "maintenance.faces";
</script>
</h:panelGroup>`

Related

How does the POST REDIRECT GET works in JSF on commandButton

I was testing couple of new features of JSF and I came across Post Redirect Get.
I wanted to redirect from my first page say first.xhtml to second.xhtml.
I have a number as a property in both the managed beans and I wanted to pass it to the second bean from the first bean using request parameter.
This is my first page
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<head>
<title>Landing Page</title>
</head>
<body>
<h3>Enter Number</h3>
<h:form>
<h:inputText id="input" name="number" value="#{postRedirectGet.number}" />
<h:commandButton value="redirect to result"
action="resultPage?faces-redirect=true&includeViewParams=true">
</h:commandButton>
</h:form>
</body>
</html>
And in the second page I have
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<f:metadata>
<f:viewParam name="number" value="#{postRedirectResult.number}"/>
</f:metadata>
<head>
<title>Result Page</title>
</head>
<body>
<h:form>
<h:outputText value="Number #{postRedirectGet.number}" />
<h:outputText value="Number #{postRedirectResult.number}" />
<h:commandButton value="Redirect to index" action="/index?faces-redirect=true" />
</h:form>
</body>
</html>
Now the page is doing a POST using commandButton and then redirecting to second page from first but it passes number=0 in the URL. It works if I change
<f:viewParam name="number" value="#{postRedirectResult.number}"/>
to
<f:viewParam name="number" value="#{postRedirectGet.number}"/>
but I thought the viewParam is used to set the value to a bean and not used to pass the values in URL. Could someone please explain how can we do POST and set the property of the managed bean on next page.
The problem is that the f:viewParam is used in two different ways in two scenarios . In h:link it is used to set the property of target bean , in h:commandButton it is used to compute the GET URL and then the target bean property can be set using #ManagedProperty . Is my understanding correct or can the f:viewParam be used to set the property in h:commandButton POST redirect get also.
What you seem to be missing is what includeViewParams does. Let me quote this very informative article (you should read all of it):
The other special query string parameter, includeViewParams, tells the navigation handler to include the view parameters when performing the navigation. But what view parameters should be included? The view parameters to be included when performing the navigation are declared on the to-view-id page.
So JSF looks at your resultpage.xhtml to determine which parameters to pass. And then dutifully proceeds to pass the current value of postRedirectResult#number (which at this time is unset/0).
To have the GET number parameter reflected in your bean, pass it as a real parameter:
<h:commandButton value="redirect to result"
action="resultPage?faces-redirect=true&number=4" />
See also:
What can <f:metadata>, <f:viewParam> and <f:viewAction> be used for?
There are different 4 ways to transfer data from JSF Page To Backing Bean.
We can use
f:param
f:setPropertyActionListener
f:attribute
Method expression (JSF 2.0).
Here you can try f:setPropertyActionListener as..
<h:commandButton value="redirect to result"
action="resultPage?faces-redirect=true">
<f:setPropertyActionListener target="#{postRedirectResult.number}" value=4 />
</h:commandButton>
Here is the link for this.

JSF Component IDs ignored when dynamically generating Views

I ran into a bit of a problem.
I generate Views dynamically by iterating a list of strings via the c:forEach tag
and then including them via the facelets include tag.
This works fine for building the layout but shows some strange behaviour.
I have a primefaces tabview containing 2 tabs.
For the first Tab (the one that gets initially displayed) the component id is set (eg. tabview:categoryTab) but that's not the case for e.g. the second tab, here I only get tabview: for the component id (but it actually should be tabview:usrTab)
Why does JSF override the id I set for the second Tab? Am I missing some crucial information from the spec?
I use JSF 2 with Primefaces 3.6 (snapshot build)
(And yes, I use the snapshot build on purpose and have tested this with stable PF releases as well but the same behaviour occurs)
edit
Code:
admin.xhtml
<ui:composition template="/templates/commonLayout.xhtml">
<ui:define name="content">
<p:panel id="parentPanel">
<h:outputText value="Verwaltung" />
<br />
<p:tabView id="tabview">
<!-- insert marker -->
<c:forEach items="#{adminTabs}" var="tab">
<ui:include src="#{tab}" />
</c:forEach>
</p:tabView>
</p:panel>
</ui:define>
</ui:composition>
catTab.xhtml
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:p="http://primefaces.org/ui"
xmlns:ui="http://java.sun.com/jsf/facelets">
<p:tab title="Categories Tab" id="catTab">
....
</p:tab>
</ui:composition>
usrTab.xhtml
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:p="http://primefaces.org/ui"
xmlns:ui="http://java.sun.com/jsf/facelets">
<p:tab title="Users Tab" id="usrTab">
....
</p:tab>
</ui:composition>
testTab.xhtml
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:p="http://primefaces.org/ui"
xmlns:ui="http://java.sun.com/jsf/facelets">
<p:tab title="TestTab" id="testTab">
....
</p:tab>
</ui:composition>
ContentProvider.java
public class ContentProvider {
....
#Produces
#Named("adminTabs")
public List<String> getTabs(){
List<String> components = new ArrayList<String>();
components.add("/templates/tabs/catTab.xhtml");
components.add("/templates/tabs/usrTab.xhtml");
components.add("/templates/tabs/testTab.xhtml");
return components;
}
....
}
This gets generated:
<ul class="ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all" role="tablist">
<li class="ui-state-default ui-tabs-selected ui-state-active ui-corner-top" aria-expanded="true" role="tab">
Categories
</li>
<li class="ui-state-default ui-corner-top" aria-expanded="false" role="tab">
TestTab
</li>
<li class="ui-state-default ui-corner-top" aria-expanded="false" role="tab">
Benutzer und Rollen
</li>
</ul>
So, to reiterate: Only the id for the first tab is retained, the ids for the other tabs are generated despite being set in the xhtml code...
I ran into something very similar with Richfaces 4 and JSF 2. I managed to hack around it by including an EL expression in the id attribute. For me
<h:form id="staticName">
was being rendered into
<form id="j_idblah>
but once I did
<h:form id="#{_objectInContext}">
it began rendering properly. Pretty hacky, but for now it'll work. Good luck!!
I ran into the same problem when including facelets with a c:forEach loop.
What worked for me was specifying the Ids as EL-Constants: id="#{'address_street'}"

PrimeFaces v3.5: Dialog is not shown using RequestContext on non-Ajax calls

In the User Guide, it is stated that RequestContext will work on both Ajax and non-Ajax calls. However, all of the examples in the User Guide are using Ajax and in my case, it doesn't work with non-Ajax calls.
The following is a test page:
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:p="http://primefaces.org/ui">
<h:head>
<title>Test page</title>
</h:head>
<h:body>
<h:form >
<p:commandButton ajax="false" value="Non-Ajax" actionListener="#{mrBean.show}" />
<p:commandButton value="Ajax" actionListener="#{mrBean.show}" />
</h:form>
<p:dialog modal="true" id="statusDialog" widgetVar="statusDlg" closable="false" >
<h:outputText value="Helllooo" />
</p:dialog>
</h:body>
</html>
And this is the managed bean:
#ManagedBean
#RequestScoped
public class MrBean {
public void show() {
System.out.println("SHOW DIALOG");
RequestContext context = RequestContext.getCurrentInstance();
context.execute("statusDlg.show();");
}
}
If I click the Ajax button, the dialog is shown correctly. However, the Non-Ajax button did nothing. In both case, the SHOW DIALOG message was printed on the console.
I'd be very grateful if you could show me how to tackle this problem :).
Best regards,
James Tran
I must disappoint you, but RequestContext in non AJAX request is available but only useful function is isAjaxRequest() with boolean return value. In documentation and API reference you can see that for all other method it is told that they are for AJAX requests. Also for execute() method:
Execute a javascript after current ajax request is completed.
At past, RequestContext wasn't available at all, now it is just available for possible future use with non AJAX requests. Additionally see this issue

JSF2 commandButton; AJAX or non AJAX, it refreshed the whole page

this is a simple example which demonstrates the case.
you have a form with a Panel and two commandButton, one is AJAX the other is not. by clicking on any of them, an InputText will be created in the backing bean and added to the Panel.
My managed bean:
#ManagedBean
public class DynamicPanel {
private Panel dynmaic;
public Panel getDynmaic() {
return dynmaic;
}
public void setDynmaic(Panel dynmaic) {
this.dynmaic = dynmaic;
}
public String adddynamic(){
InputText text = new InputText();
dynmaic.getChildren().add(text);
text.setValue(text.getId()+" Size= "+ dynmaic.getChildren().size());
return null;
}
public String removeall(){
this.dynmaic.getChildren().clear();
return null;
}
}
My XHTML page
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html" xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:p="http://primefaces.org/ui" xmlns:f="http://java.sun.com/jsf/core">
<h:head>
<title>Facelet Title</title>
</h:head>
<h:body>
<h:form>
<p:panel id="dynamic" binding="#{dynamicPanel.dynmaic}">
</p:panel>
<h:commandButton value="Add with AJAX" id="ajaxBtn" >
<f:ajax onevent="onClick" execute="#{dynamicPanel.adddynamic()}" render="dynamic" />
</h:commandButton>
<h:commandButton value="Add" action="#{dynamicPanel.adddynamic}" />
<h:commandButton value="remove all" action="#{dynamicPanel.removeall}" />
</h:form>
</h:body>
</html>
my faces-config.xml is empty.
Now, I have three issues with the code above. Could someone please clarify it to me, I'm new to JSF2.
the first is, why both command buttons behave the same? clicking on ether one would refresh the whole page.
the second issue is, why clicking on the non AJAX commandButton adds two Inputfieds at a time?
the third is, why changing the scope of the managed bean to #SessionScoped will give an error once you load the page? ( somehow just loading the page, the form issues an ajax request without me clicking on the commandButton. Why is that?
Try the following, that should work better.
<h:commandButton value="Add with AJAX" id="ajaxBtn" >
<f:ajax onevent="click" execute="ajaxBtn"
render="dynamic"
listener="#{dynamicPanel.adddynamic()}"
</h:commandButton>
As far as I know, for f:ajax execute attribute the id of the components should be given, and you should call methods such as addDynamic() in listener attribute.
When you click on the non-ajax button, it posts the whole form which also includes your ajax command. so basically your addDynamic() function is called twice, one through ajax command and the second through non-ajax command..

Partial rendering JSF components

dwelling over how to partial render (divs), by including different source files (with panels and components). Depending on menu actions. If understood the JSF phases correctly, the View is rebuilt during the Render Response, the last phase. And if I have events and actions, they will be invoked during the Invoke Application phase, the phase before.
All I want to do is to set the including xhtml page for a specific menu command via ajax, before the View is re-rendered. But the ui:include always get invoked before the menu action. I've tried with richfaces 4 (a4j:param, rich:panel, etc) and standard JSF 2.0 (f:param, h:panelGroup) components, but the the ui:include always get invoked before the action.
What should I do to process the menu action (to set the including page), before the ui:include gets invoked?
PS. This must be the standard patter, instead of including static content. But I find very few examples on this on the net ?!
Menu.xhtml
<rich:toolbar itemSeparator="line">
...
<rich:dropDownMenu mode="ajax">
<f:facet name="label">
<h:panelGroup>
<h:outputText value="Menu 1" />
</h:panelGroup>
</f:facet>
<rich:menuItem id="newActivityMenu" action="#{navigationBean.menuAction}" render="content" label="New">
<a4j:param id="newActivityParam" name="includeContentPage" value="/user/Create.xhtml" />
</rich:menuItem>
...
NavigationBean.Java
#ManagedBean
#RequestScoped
public class NavigationBean {
public String menuAction() {
String param = JsfUtil.getRequestParameter("includeContentPage");
this.includedContentPage = param;
JsfUtil.log(this, "Including Content Page : " +param);
FacesContext.getCurrentInstance().renderResponse();
return "";
}
public String getIncludedContentPage() {
if(includedContentPage == null)
return "";
else if(!includedContentPage.endsWith(".xhtml"))
includedContentPage += ".xhtml";
JsfUtil.log(this, "Get Content Page : " +includedContentPage);
return includedContentPage;
}
layoutClient.xhtml
...
<ui:define name="top">
<ui:include src="/resources/viewComponents/menuTop.xhtml"/>
</ui:define>
<ui:define name="content">
<ui:include src="#{navigationBean.includedContentPage}"/>
</ui:define>
...
masterLayout.xhtml (added)
...
<h:body>
<div id="top" >
<ui:insert name="top">Top Default</ui:insert>
</div>
<div id="left">
<ui:insert name="left">Left Default</ui:insert>
</div>
<ui:insert name="content">Content Default</ui:insert>
</h:body>
..
You must have a template page as well since you are defining top and content in layoutClient.xhtml so I think you are trying to be too general with the layoutClient.xhtml page as it appears to be functioning as a template as well. Lets assume your template page is called template.xhtml. The standard pattern you eluded to is to make your template page something like this:
template.xhtml
...
<ui:insert name="top">
<ui:include src="/resources/viewComponents/menuTop.xhtml"/>
</ui:insert>
...
<ui:insert name="content" />
...
This means that all your pages contain the menu at the 'top' (by default, they can override this) and that they must specify their own content, which makes sense.
Now, instead of trying to make a page like layoutClient.xhtml that does tricky stuff to determine which content is inserted, create each page seperately like this:
page1.xhtml
<ui:composition template="template.xhtml">
...
<ui:define name="content">
<p>This is a page that defines some content and also includes my menu that it inherits from template.xhtml</p>
</ui:define>
...
</ui:composition>
page2.xhtml
<ui:composition template="template.xhtml">
...
<ui:define name="content">
<p>This is another page that defines some content and also includes my menu that it inherits from template.xhtml</p>
</ui:define>
...
</ui:composition>
Both of these pages inherit your menu and put the content in the appropriate place.
With that kind of configuration, all your menuAction() method needs to do is return a link to page1.xhtml or page2.xhtml. Also, you don't need any complex use of parameters or manual calls to renderResponse() or a4j:param tags!

Categories