Liferay Primefaces Portlet redirection issue - jsf-2

I am currently creating a primfaces portlet. From my view.xhtml when I am calling Submit method in my bean class. I want to redirect the view base on input.
Below is my code snippet of Submit method:
Submit(){
try {
FacesContext.getCurrentInstance().getExternalContext().redirect("/views/Success.xhtml");
} catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
It simply adds to the local host URL as http://localhost:8081/views/Success.xhtml. I guess I am missing something critical probably. Should implement some render phase method if yes how do i go about it so that it created a render url for that page.

Why aren't you using normal JSF navigation for this? No need to bother with portlet URLs in that case, because it will be handled by the JSF bridge for you.
public String submit() {
// do stuff;
return "/views/Success";
}
You can omit the .xhtml extension.

You can create the url using the following code
FacesContext facesContext = FacesContext.getCurrentInstance();
ExternalContext externalContext = facesContext.getExternalContext();
PortletRequest portletRequest = (PortletRequest) externalContext
.getRequest();
ThemeDisplay themeDisplay = (ThemeDisplay) req.getAttribute(WebKeys.THEME_DISPLAY);
PortletURL url = PortletURLFactoryUtil.create(req,
PortalUtil.getPortletId(req),
themeDisplay.getLayout().getPlid(),
PortletRequest.ACTION_PHASE);
url.setParameter("_facesViewIdRender", "/views/Success.xhtml");
url.setWindowState(WindowState.NORMAL);
url.setPortletMode(PortletMode.VIEW);
FacesContext.getCurrentInstance().getExternalContext().redirect(url.toString());

Related

Handle ViewExireException/ajax and display a Primefaces dialog

I don't redirect or forward my user to another page. So when the my SessionExpiredExceptionHandler (extends ExceptionHandlerWrapper) handles the ViewExireException. I want the user to stay on the same page and display a PrimeFaces Dialog. For notifying that the session has expired and that the user needs to login again (dialog based). I am use Servlet 3.1 functions to login/logout user and Basic/file for auth-method to map the users to different system roles.
What is happening now is that the View/page get refreshed after 2 min, but the session doesn't get invalidated. That only happens the second time when the page refreshes, after 4 min.
<session-config>
<session-timeout>2</session-timeout>
</session-config>
Edit:
Which is refreshed by the meta tag:
<meta http-equiv="refresh" content="#{session.maxInactiveInterval}" />
How can I make SessionExpiredExceptionHandlerinvalidate the session object (Servlet logout) when the Exceptions occur the first time, and how can I invoke a JavaScript (expireDlg.show()) on the client to display a PrimeFaces dialog ?
I have looked at some other threads but not found a viable solution.
Session time-out
SessionExpiredExceptionHandler
#Override
public void handle() throws FacesException {
for (Iterator<ExceptionQueuedEvent> i = getUnhandledExceptionQueuedEvents().iterator(); i.hasNext();) {
ExceptionQueuedEvent event = i.next();
ExceptionQueuedEventContext context = (ExceptionQueuedEventContext) event.getSource();
Throwable t = context.getException();
if (t instanceof ViewExpiredException) {
ViewExpiredException vee = (ViewExpiredException) t;
FacesContext fc = FacesContext.getCurrentInstance();
Map<String, Object> requestMap = fc.getExternalContext().getRequestMap();
NavigationHandler nav = fc.getApplication().getNavigationHandler();
try {
requestMap.put("currentViewId", vee.getViewId());
nav.handleNavigation(fc, null, "Home");
fc.renderResponse();
} finally {
i.remove();
}
}
}
// At this point, the queue will not contain any ViewExpiredEvents.
// Therefore, let the parent handle them.
getWrapped().handle();
}
web.xml
<exception-type>javax.faces.application.ViewExpiredException</exception-type>
<location>/home.xhtml</location>
</error-page>
How can I make SessionExpiredExceptionHandler invalidate the session object (Servlet logout) when the Exceptions occur the first time
The session is supposedly to be already invalidated/expired (otherwise a ViewExpiredException wouldn't be thrown at all), so I don't see how it's useful to manually invalidate/expire it yourself. But for the case that, you can invalidate it as follows:
externalContext.invalidateSession();
and how can I invoke a JavaScript (expireDlg.show()) on the client to display a PrimeFaces dialog ?
You can use the PrimeFaces RequestContext API to programmatically instruct PrimeFaces to execute some JS code on complete of ajax response.
RequestContext.getCurrentInstance().execute("expireDlg.show()");
Don't forget to remove the navigation handler block from the exception handler if you actually don't want to navigate.
This solution worked for my case. It seams that Primefaces (3.3) is swallowing the ExceptionQueuedEvent. There are no Exception to handle when my ViewExceptionHandler gets called. So instead I used the p:idleMonitor component with event listner. I also removed the meta refresh tag.
<p:idleMonitor timeout="#{(session.maxInactiveInterval-60)*1000}">
<p:ajax event="idle" process="#this" update="sessionMsg" listener="#{userController.userIdleSession()}" />
<p:ajax event="active" process="#this" update="sessionMsg" listener="#{userController.userActiveSession()}"/>
</p:idleMonitor>
One weird thing is if the timeoutis excatly the same as the web.xmlsession time-out parameter, the listener won't be invoked.
Bean functions
public void userIdleSession() {
if (!userIdleMsgVisable) {
userIdleMsgVisable = true;
JsfUtil.addWarningMessage(JsfUtil.getResourceMessage("session_expire_title"), JsfUtil.getResourceMessage("session_expire_content"));
}
}
public void userActiveSession() {
if (!userSessionDlgVisable) {
userSessionDlgVisable = true;
RequestContext.getCurrentInstance().execute("sessionExipreDlg.show()");
}
}
The dialog (sessionExipreDlg) called the redirect instead of using navigation handler to get new scope and refresh the page.
public void userInactiveRedirect() {
FacesContext fc = FacesContext.getCurrentInstance();
userIdleMsgVisable = false;
userSessionDlgVisable = false;
sessionUser = null;
HttpServletRequest request = (HttpServletRequest) fc.getExternalContext().getRequest();
JsfUtil.findBean("homeController", HomeController.class).clearCurrentValues();
try {
fc.getExternalContext().redirect(JsfUtil.getApplicationPath(request, false, null));
} catch (IOException ex) {
BeanUtil.severe(ex.getLocalizedMessage());
}
}

How to redirect xhtml page to the same Request Scoped Bean?

Since the bean is in Request Scope, redirect creates a new instance.
If I try to put the bean in the View Scope, then I get the error of Property name is referenced to object narrower than the target View.
I'm displaying a tree, and when the user clicks on the tree node, say the 2nd node, the values corresponding to that node is displayed on the next page.
#ManagedBean
#RequestScoped
public class ThreeSixtyDegreeBean implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
#ManagedProperty(value="#{param.name1}")
private String name;
private String type;
private String typeName;
private List<AttributeDetails> attributeList;
private List<Entity> entityList;
private boolean rendered;
private TreeNode root;
private TreeNode selectedNode;
public void onNodeSelect() {
*selecting Data of the Node selected*
String a=fetchData();
try {
FacesContext.getCurrentInstance().getExternalContext()
.redirect(a);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Where a is name of the xhtml page.
You can stash your stuff in the new JSF 2 Flash Scope to stash attributes between requests. Your onNodeSelect() can now look like this :
public void onNodeSelect() {
*selecting Data of the Node selected*
FacesContext context = FacesContext.getCurrentInstance();
Flash flash = context.getCurrentInstance().getExternalContext().getFlash(); //prepare jsf flash scope, to store user data pojo for the next view
flash.putNow("myData", fetchData());
try {
FacesContext.getCurrentInstance().getExternalContext()
.redirect(a);
} catch (IOException e) {
e.printStackTrace();
}
}
}
On the destination page, you can then retrieve the data you stored in the flash scope using the #{flash} EL expression. It's essentially a Map so you just use the key of the value you stored ("myData" in the example I used above). to access it, use
#{flash.myData.someMemberVariable}
The view scope wouldn't have worked either. It lives as long as you're postbacking to the same view. A redirect basically creates a brand new GET request and would also have recreated the view scope.
In this particular case, you're better off performing data initialization in the redirected request, not in the postback request. You could do that by creating a normal GET link wherein you pass the necessary information as request parameters. Something like this:
<h:link value="#{node.name}" outcome="#{node.viewId}">
<f:param name="someId" value="#{node.someId}" />
<f:param name="name1" value="#{param.name1}" />
</h:link>
and then in the request scoped bean associated with the redirected page, you can just use #ManagedProperty or even the <f:viewParam> to set the request parameters as model values.
See also:
Communication in JSF 2.0 - Processing GET request parameters

Handling viewExpiredexception by way of redirection

I've been trying to cache and handle viewExpiredexception where if a `viewExpiredexceptionviewExpiredexception is throw, i have written a custom viewExpiredexception Handler which is suppose to redirect back to the page where the Exception is thrown, i also insert a boolean into session which is used in the redicted page to show "page was refreshed" message.
Below is the relevant part of my ViewExpiredException Handler:
public void handle() throws FacesException {
for (Iterator<ExceptionQueuedEvent> i = getUnhandledExceptionQueuedEvents().iterator(); i.hasNext();) {
ExceptionQueuedEvent event = i.next();
ExceptionQueuedEventContext context = (ExceptionQueuedEventContext) event.getSource();
Throwable t = context.getException();
if (t instanceof ViewExpiredException) {
ViewExpiredException vee = (ViewExpiredException) t;
FacesContext fc = FacesContext.getCurrentInstance();
Map<String, Object> requestMap = fc.getExternalContext().getRequestMap();
NavigationHandler nav = fc.getApplication().getNavigationHandler();
try {
requestMap.put("currentViewId", vee.getViewId());
HttpServletRequest orgRequest = (HttpServletRequest) fc.getExternalContext().getRequest();
fc.getExternalContext().redirect(orgRequest.getRequestURI());
Map<String, Object> sessionMap = FacesContext.getCurrentInstance().getExternalContext().getSessionMap();
sessionMap.put("refreshedMaxSesion",true);
fc.renderResponse();
}
catch(IOException e){ }
finally { i.remove(); }
}
}
// here queue will not contain any ViewExpiredEvents, let the parent handle them.
getWrapped().handle();
}
and the Navigation case
<navigation-rule>
<navigation-case>
<from-outcome>/app/ord1</from-outcome>
<to-view-id>/jsp/orderHist.jsp</to-view-id>
<redirect />
</navigation-case>
</navigation-rule>
I had limited success with the above, it would work in some cases but in other cases the page wouldn't redirect at all. It works mostly in chrome but in IE it didn't work at all.
I tried making few changes such as using
HttpServletResponse response = (HttpServletResponse) fc.getExternalContext().getResponse();
response.sendRedirect(vee.getViewId());
but i was getting the 500 error pages with exception saying View must Exists... so i stopped experimenting to find out what i am doing wrong first. How can this goal of cahcing a ViewExpiredException, and Redirectign back to the page where the error was thrown be archived?
I'm on myFaces (2.1.7) and richFaces (3.3.3). Thanks
There is some work already done inside MyFaces Community to deal with ViewExpiredException in a graceful way. Some issues has been solved in MyFaces Core (see MYFACES-3530 and MYFACES-3531) Maybe you should try the latest snapshot HERE.

getRequestDispatcher(" ").forward does not work in JSF 2.0

I have following codes in my app which is using JSF 2.0
HttpServletRequest request = (HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext()
.getRequest();
HttpServletResponse response = (HttpServletResponse)FacesContext.getCurrentInstance().getExternalContext().getResponse();
......
try {
request.getRequestDispatcher("SomePage.xhtml").forward(request, response);
} catch (ServletException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
But when ever the line ..
request.getRequestDispatcher("SomePage.xhtml").forward(request, response);
is executed I'm getting the following exception...
java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
at java.util.ArrayList.RangeCheck(ArrayList.java:547)
at java.util.ArrayList.get(ArrayList.java:322)
at javax.faces.component.AttachedObjectListHolder.restoreState(AttachedObjectListHolder.java:165)
at javax.faces.component.UIComponentBase.restoreState(UIComponentBase.java:1432)
at com.sun.faces.application.view.StateManagementStrategyImpl$1.visit(StateManagementStrategyImpl.java:265)
at com.sun.faces.component.visit.FullVisitContext.invokeVisitCallback(FullVisitContext.java:151)
Due to some appilcation constaints I can not use the
FacesContext.getCurrentInstance().getExternalContext().redirect();
method..
Is it a JSF 2.0 bug??
I never tried to send a redirect this way because this is not the standard solution for navigation in JSF2.
Here are some methods that will work correctly in a JSF 2.0 environment:
1. Specify the redirect command in the navigation link
Attach ?faces-redirect=true to your navigation and this should do the job.
public String someAction(){
// Logic here
return "newPage" + "?faces-redirect=true"
}
<h:form id = "form">
<h:inputText>...</h:inputText>
<h:commandButton action = "#{controller.someAction}" />
</h:form>
After processing the Login in the someAction the navigation flow will be redirected to newPage.xhtml. All you have to do is to correctly call the action from your UI Form.
2. Specify the redirect through external context
This method is closer to what you are looking for:
public void someAction(){
// Logic here
try{
FacesContext.getCurrentInstance().getExternalContext().redirect("newPage");
} catch (Exception e){
e.getMessage();
}
}
First of all, whenever you need to use javax.servlet.* classes inside a JSF managed bean, then changes are big that you're doing things the wrong way or unnecessarily overcomplicated. Keep this in mind for the next time you need to a add a import javax.servlet...; line.
As to your concrete problem, in your particular case, you should just be returning the view ID in order to perform a forward.
public String someAction() {
// ...
return "SomePage";
}
An alternative is ExternalContext#dispatch() method, but this is also unnecessarily overcomplicated.

JSF 2 redirection problem

I have following two methods in my backing bean -
public String validateUser() {
FacesContext facesCtx = FacesContext.getCurrentInstance();
if(userName.equals("user1") && password.equals("pass1")) {
User user = new User();
user.setUserName(userName);
HttpSession session = (HttpSession) facesCtx.getExternalContext().getSession(false);
session.setAttribute(User.SESSION_ATTRIBUTE, user);
return "secured/home.jsf?faces-redirect=true";
}
if(!userName.equals(LoginBean.USERNAME)) {
FacesMessage msgForUserName = new FacesMessage(FacesMessage.SEVERITY_ERROR, "Username did not match.", null);
facesCtx.addMessage("loginForm:userName", msgForUserName);
}
if(!password.equals(LoginBean.PASSWORD)) {
FacesMessage msgForPassword = new FacesMessage(FacesMessage.SEVERITY_ERROR, "Password did not match.", null);
facesCtx.addMessage("loginForm:password", msgForPassword);
}
return null;
}
public String logout() {
logger.info("Logging out .........................................");
FacesContext facesCtx = FacesContext.getCurrentInstance();
HttpSession session = (HttpSession) facesCtx.getExternalContext().getSession(false);
session.invalidate();
return "login.jsf?faces-redirect=true";
}
I don't know why the redirection is working in the first method (i.e. validateUser()), but it's not working in the second method (i.e. logout()).
The code inside the logout method is actually executed, the session also gets invalidated,but somehow the browser stays on the same page.
And, I am using PrimeFaces p:commandButton and the ajax is enabled on both of them.
Any one, any idea?
Thank you.
but somehow the browser stays on the same page. And, I am using PrimeFaces p:commandButton and the ajax is enabled on both of them
I wouldn't expect it to fail. I suspect that this has something to do with the invalidated session. Try it with ajax="false" on the <p:commandButton>.
Unrelated to the problem, you should try to minimize the javax.servlet imports in your JSF managed beans. They often indicate that you're doing things in the wrong place or the clumsy way. In pure JSF2, you can invalidate the session as follows:
FacesContext.getCurrentInstance().getExternalContext().invalidateSession();
You can get/set objects in the session by the session Map.
Map<String, Object> sessionMap = FacesContext.getCurrentInstance().getExternalContext().getSessionMap();
// ...
Or just make it a managed bean (property).
See also:
How can I create a new session with a new User login on the application?

Resources