Struts2 IllegalStateException on first visit - session

I have a protected page that is supposed to redirect to the login screen when not logged in. The issue is that when i try to view that page, instead of redirecting to login it crashes with a IllegalStateException error. The kicker is that if I manually visit the login page it loads, then I try to visit the protect page again and it will redirect as expected. It must be some sort of session caching issue because the problem will never happen again unless i restart the browser. After a browser restart the issue will continue until I manually visit the login page again. And of course the error reporting is super vague to what the issue actually is
Aug 20, 2013 4:36:40 PM org.apache.catalina.core.StandardWrapperValve invoke
SEVERE: Servlet.service() for servlet default threw exception
java.lang.IllegalStateException
at org.apache.catalina.connector.ResponseFacade.sendError(ResponseFacade.java:407)
at org.apache.struts2.dispatcher.Dispatcher.sendError(Dispatcher.java:852)
at org.apache.struts2.dispatcher.Dispatcher.serviceAction(Dispatcher.java:534)
at org.apache.struts2.dispatcher.ng.ExecuteOperations.executeAction(ExecuteOperations.java:77)
at org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter.doFilter(StrutsPrepareAndExecuteFilter.java:91)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.springframework.orm.hibernate3.support.OpenSessionInViewFilter.doFilterInternal(OpenSessionInViewFilter.java:198)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
at org.apache.catalina.core.StandardContextValve.__invoke(StandardContextValve.java:191)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java)
at org.apache.catalina.core.StandardHostValve.__invoke(StandardHostValve.java:127)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:298)
at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:857)
at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:588)
at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:489)
at java.lang.Thread.run(Thread.java:662)
My Interceptor that I use to validate if they are logged in and redirect when neccessary. the very first return BaseAction.Login hits like its supposed to, and when I enable logging the struts2 loggers indicate a redirect to viewLogin is going to happen, but it doesn't. I just crashes instead:
public String intercept(ActionInvocation invocation) throws Exception {
BaseAction action = (BaseAction)invocation.getAction();
LoginManager loginManager = (LoginManager)action.getSession().get("loginManager");
Boolean loggedIn = false;
final ActionContext context = invocation.getInvocationContext();
ActionMapping mapping = (ActionMapping)context.get(ServletActionContext.ACTION_MAPPING);
if(loginManager == null && action.isLoginRequired()) {
//user is not logged in and login required, redirect them to login module
return BaseAction.LOGIN;
} else if(loginManager != null && action.isLoginRequired()) {
//logged in, make sure they are accessing a valid resource
String loginKey = action.getSessionLoginVarable(mapping);
if(loginManager.getLoginPaths().containsKey(loginKey)) {
return invocation.invoke();
} else {
//return to login which will redirect them to appropriate path
return BaseAction.LOGIN;
}
} else {
//public access to continue with action
return invocation.invoke();
}
}
Struts-config file
<package name="student" namespace="/student" extends="struts-default-custom">
<default-action-ref name="viewLogin" />
<global-results>
<result name="login" type="redirectAction">viewLogin</result>
</global-results>
<action name="*Login" method="{1}" class="controller.shared.StudentLogin">
<interceptor-ref name="noLoginStack"/>
<result type="redirectAction">viewSearchAdvisors</result>
<result name="input">/student/student-login.jsp</result>
<result name="error" type="redirectAction">viewLogin</result>
</action>
So just to summaraize. First visit to this protected page on browser startup it crashes instead of redirect to login. I manually visit login page, then try to visit protected page no crash and it redirects back to login like its supposed to. It will continue to work fine until i restart the browser.
any ideas??

This issue is resolved. I have absolutely no idea why, but when I changed my struts xml files to use this DTD file
http://struts.apache.org/dtds/struts-2.3.dtd
instead of
http://struts.apache.org/dtds/struts-2.1.dtd
the issue went away. I was grasping at straws checking every configuration file against config files of other apps i have that use the same process without issue and saw this. I decided what the hell and made the change...and it works.

Related

partial-response on session expire

We have a JSF 2.0, Primefaces 5.0, Spring Security 3.2.3.RELEASE application.
To handle session timeout, I am using primefaces idleMonitor and p:dialog & javascript to display a countdown popup and redirect them back to login page.
I have also implemented a custom CacheControlPhaseListener so that the pages are not cached. I set the no-cache in the response headers in the CacheControlPhaseListener.
<lifecycle><phase-listener id="nocache">com..filter.CacheControlPhaseListener</phase-listener></lifecycle>
I also have error handling configured in my web.xml:
<error-page>
<exception-type>javax.faces.application.ViewExpiredException</exception-type>
<location>/error.jsf</location></error-page>
I have also implemented a ViewExpiredHandler that extends ViewHandlerWrapper
#Override
public UIViewRoot restoreView(FacesContext ctx, String viewId)
{
UIViewRoot viewRoot = super.restoreView(ctx, viewId);
try
{
if (viewRoot == null)
{
viewRoot = super.createView(ctx, viewId);
ctx.setViewRoot(viewRoot);
}
}
catch (Exception e)
{
e.printStackTrace();
}
return viewRoot;
}
The problem I am still having is:
1. When the session expires on a idle page (E.g. Search page) and if some ajax action is triggered on a page, even though I logout, when I navigate back to the page (e.g. Login-> Home-> Search page). I see a partial-response xml error:
<partial-response><changes><update id="blGridId"><table id="blGridId" style="width:100%;">
<tbody>
<tr>
<td><div id="blTableId" class="ui-datatable ui-widget ui-datatable-scrollable ui-datatable-resizable"><div id="sublTableId_paginator_top" class="ui-paginator ui-paginator-top ui-widget-header ui-corner-top" role="navigation"><span class="ui-paginator-prev ui-state-default ui-corner-all ui-state-disabled"><span class="ui-icon ui-icon-seek-prev">p</span></span><span class="ui-paginator-next ui-state-default ui-corner-all ui-state-disabled"><span class="ui-icon ui-icon-seek-next">p</span></span></div><div class="ui-widget-header ui-datatable-scrollable-header"><div class="ui-datatable-scrollable-header-box"><table role="grid"><thead id="blTableId_head"><tr role="row"><th id="blTableId:j_idt101" class="ui-state-default ui-resizable-column" role="columnheader" style="width:34px; #width:37px;"><span class="ui-column-title"><span style="word-wrap: break-word;white-space: normal;">Client </span></span></th><th id="blTableId:j_idt104" class="ui-state-default
2. If I hit a browser refresh, it loads back the page and I can continue with the actions.
Please let me know what I need to do in addition to the above to resolve the partial-response error. Do I need to add a servlet filter to invalidate the session?
I would really appreciate any help and feedback on this as it is high priority.
I had got the same issue when session had been expired. I thought it was too late, but maybe would be helpful for others who has issues like me.
The root cause is Spring Security saves the last request before redirecting client to do the authentication. After then, Spring security would try to perform the request again when user visits the page of last request. Unfortunately, the request was ajax/partial and its view was expired -> partial xml content was returned.
Easy way to get rid of this issue is removing the saving behavior of Spring Security. SavedRequestAwareAuthenticationSuccessHandler class is used to handle these kind of behaviours. Configure as:
<bean id="authenticationFilter" class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter"
p:authenticationManager-ref="authenticationManager"
p:authenticationFailureHandler-ref="authenticationFailureHandler"
p:authenticationSuccessHandler-ref="authenticationSuccessHandler"
p:usernameParameter="username"
p:passwordParameter="password">
</bean>
...
<bean id="authenticationSuccessHandler" class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler"
p:defaultTargetUrl="/"
p:alwaysUseDefaultTargetUrl="true"/>
Hope it would help.

Invalidate Http Session in JSF2 on Logout

Version :
Apache MyFaces 2.1.14
Rich Faces 4.3.5
Issue :
I want to invalidate JSF session when user clicks on Logout page.
I have decided to use PreRenderViewEvent for the same.
Below is the design for the same :
When logout page is being rendered , call a listener method in back end bean through PreRenderViewEvent.
Invalidate JSF session in java method like : FacesContext.getCurrentInstance().getExternalContext().invalidateSession();
My question is : is this a right approach in invalidating session ? Or is there any better approach available ?
Code :
<!-- call this in Logout.xhtml page -->
<f:event listener="#{bean.invalidateSession}" type="preRenderView" />
EDIT 1:
Let me elaborate the context of the question.
I have two web applications in my system.
When a user clicks on Logout button it always goes to webApp1.
The requirement is to invalidate session in webApp1 and then redirect to webApp2 where again invalidate the session.
The preRenderView event will be used when webapp1 LogOut page is being rendered. The listener there will invalidate the session and redirect to webapp2 LogOut page like below code :
public void logOut(ComponentSystemEvent event) throws IOException{
//logoutAction param will be read from request
String redirectUrl = "/webapp2/Logout.xhtml?action="+logoutAction;
FacesContext.getCurrentInstance().getExternalContext().invalidateSession();
FacesContext.getCurrentInstance().getExternalContext().redirect(redirectUrl);
}
I usually just add a button to my template :
<h:commandLink action="#{loginBean.logout()}" value="Logout" rendered="#{!empty loginBean.account}" />
This button calls my "login" manager bean, which contains an attribute Account account, populated with the user's parameters. It's also only rendered if the user is connected.
public String logout()
{
setAccount(null);
FacesContext.getCurrentInstance().getExternalContext().getSessionMap().clear();
FacesContext.getCurrentInstance().getExternalContext().invalidateSession();
NavigationUtils.redirect("/admin/login");
return "";
}

JSF AJAX and normal redirect to login and back to previous page

Current status :
If any session time out occurs, redirectToLogin function in FacesAjaxAwareUserFilter will be executed. From there I can redirect to any page I need. I get the URI in all cases.
It's all working fine and big thanks to BalusC. :)
Now the second part which makes the issue
Redirect to login and come back to the previous page.
For Eg :
Page 5----> Login------> Page 5
I have appended the redirect URI to the login URI and retrieved the values from the bean.
But the problem is that I have 2 pages before the user login. A login mode selection page (ie; google authentication or default login) and the page that reads the username and password.
How can I pass the redirect URI through both the pages.?
This is the code I have used to redirect at Ajax time out and in normal session time out.
Class FacesAjaxAwareUserFilter
if ("partial/ajax".equals(request.getHeader("Faces-Request"))) {
res.setContentType("text/xml");
res.setCharacterEncoding("UTF-8");
res.getWriter().printf(FACES_REDIRECT_XML, request.getContextPath() + getLoginUrl()+"?redirectUrlPattern="+request.getRequestURI());
}
else {
if (request.getRequestedSessionId()!=null && (!SecurityUtils.getSubject().isAuthenticated() || !request.isRequestedSessionIdValid())) {
response.sendRedirect(getLoginUrl()+"?redirectUrlPattern="+request.getRequestURI());
return;
}
super.redirectToLogin(req, res);
}
Method used is FullAjaxExceptionHandlerFactory in Omniface.
I have used a method of appending the values to the URI, but it fails to identify whether the session is expired or a session not created (when a user logs in at first).
Problem Code
if (request.getRequestedSessionId()!=null && (!SecurityUtils.getSubject().isAuthenticated() || !request.isRequestedSessionIdValid()))
I am looking for a way to identify the expired session from new session created before login.
Wish to implement this in a Better Way.
A method which will not append present URI with the redirect URL is most welcomed.
I have founded out a solution but still i don't find it as the best solution. Any other answer for doing it is greatly appreciated.
Step 1
Configure the application to detect Ajax Timeout and normal timeout. This is how i did it..!!
Step 2
Finding the URI when session timeout occurs, using this.
FacesAjaxAwareUserFilter will grab the ServletRequest and ServletResponse for you.
form that you can convert that to HttpServletRequest and HttpServletResponse using this
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response= (HttpServletResponse) res;
The request.getRequestURI() will get you the redirect URI
String redirectURI=request.getRequestURI();
Now you want to save this to some storage so that you can use it in any place you want. The only way i found is to append it with URL using this.
res.getWriter().printf(FACES_REDIRECT_XML, request.getContextPath() + "/sessionexpired"+"?redirectUrlPattern="+request.getRequestURI());
or
response.sendRedirect("/sessionexpired"+"?redirectUrlPattern="+request.getRequestURI());
Now on the page load of my sessionExpiry page bean, i used this to grab the value passed through the URL
redirectUrl=FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("redirectUrlPattern");
Now using JavaScript i set that value of redirectUrl to localStorage. This is how you can save values to local storage.
This is my sessionexpiry.xhtml page.
<script type="text/javascript">
function setRedirectURIToLocalStorage(){
if(typeof(Storage)!=="undefined")
{
localStorage.redirectURI=document.getElementById("redirectUrl").value;
window.location.href="/login";
}
}
</script>
</h:head>
<h:body id="body" onload="setRedirectURIToLocalStorage()">
<f:view contentType="text/html">
<h:form prependId="false" >
<h:inputHidden id="redirectUrl" value="#{sessionExpBean.redirectUrl}" />
<h:outputText value="Session Expired... Redirecting...!!"></h:outputText>
</h:form>
</f:view>
</h:body>
The page will call setRedirectURIToLocalStorage() function onLoad
and then it set the values to the localStorage. Thus the redirect
value is available all across the browser. You can use that in any
page you need.!! All you need to do is to check in this variable
localStorage.redirectURI

MVC3 CustomError 500 Issue When Entering HTML Tags In TextFields

I implemented a default custom error page into my MVC3 web application and it seems to be working for the most part. I can enter a bad URL and my browser is redirected to a default error page without any issues. However, I am having a problem when I add an HTML tag into a text field such as <br>. This is causing a 500 error (Internal Server Error) that is not being redirected to my default error page.
In my web.config I have the following tag.
<customErrors defaultRedirect="/CustomErrors/DefaultError" mode="On">
</customErrors>
I have a controller called CustomErrors with a view called DefaultError.cshtml.
public class CustomErrorsController : Controller
{
public ActionResult DefaultError()
{
return View();
}
}
When I enter bad URL a breakpoint I enter in the CustomErrorsController's DefaultError action method. But when I enter <br> into a text field such as the log in page I am seeing flaky behavior. IE or Mozilla shows a default browser error page. The odd thing is that when I remove the customerror property from my web.config I get an ugly page with the exception being dump on it. So my custom error is somehow being referenced but not fully implemented.
Any ideas with dealing with custom errors and entering html tags in text fields?
Thanks Adriano for pointing me in the right direction. I found this example (http://stackoverflow.com/questions/6324368/asp-net-mvc-override-onexception-in-base-controller-keeps-propogating-to-applica) and ended up implementing the OnException in my base controller since the exception would not occur in my CustomErrorsController.
protected override void OnException(ExceptionContext filterContext)
{
filterContext.Result = new ViewResult
{
ViewName = "/CustomErrors/DefaultError"
};
filterContext.ExceptionHandled = true;
}

javax.faces.application.ViewExpiredException and error page not working

I set up my web app as follows:
<error-page>
<exception-type>javax.faces.application.ViewExpiredException</exception-type>
<location>/WEB-INF/include/viewExpired.html</location>
</error-page>
so that the above error page be displayed when the viewExpiredException is thrown.
However, when I restart the server and submit an ajax request on an "left-open" page, the exception is thrown in the console and my error page is not displayed.
Here is that stack trace from the console:
javax.faces.application.ViewExpiredException: viewId:/contact.jsf - La vue /contact.jsf na pas pu être restaurée.
at com.sun.faces.lifecycle.RestoreViewPhase.execute(RestoreViewPhase.java:200)
at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
at com.sun.faces.lifecycle.RestoreViewPhase.doPhase(RestoreViewPhase.java:111)
at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:118)
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:312)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at com.ocpsoft.pretty.PrettyFilter.doFilter(PrettyFilter.java:118)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:630)
at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:436)
at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:374)
at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:302)
at com.ocpsoft.pretty.PrettyFilter.doFilter(PrettyFilter.java:110)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter.doFilterInternal(OpenEntityManagerInViewFilter.java:113)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at com.jeanbaptistemartin.util.DisableUrlSessionFilter.doFilter(DisableUrlSessionFilter.java:70)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:525)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:128)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:286)
at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:845)
at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:583)
at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:447)
at java.lang.Thread.run(Thread.java:662)
anyone has got any clue please?
Thanks in advance,
J.
So you want to handle Ajax errors on the standard JSF 2.0 implementation? The standard JSF implementation doesn't provide a ready-to-use component for this. In PrimeFaces for example, you could use <p:ajaxStatus> component for this. The JSF 2.0 specification however outlines the following in chapter 13.3.6.2:
13.3.6.2 Handling Errors For All Ajax Requests
The JavaScript API provides the jsf.ajax.addOnError function that can be used to register a JavaScript function that will be notified when an error occurs for any Ajax request/response. Refer to Section 14.4 “Registering Callback Functions” for more details. The jsf.ajax.addOnError function accepts a JavaScript function argument that will be
notified when errors occur during any Ajax request/response cycle. [P1-start-event] The implementation must ensure the
JavaScript function that is registered must be called in accordance with the errors outlined in Section TABLE 14-5
“Errors”
You can find here a blog of one of the Mojarra developers which contains basic examples how to show the ajax status. Here's an extract of relevance:
<h3> Status:</h3>
<textarea id="statusArea" cols="40" rows="10" readonly="readonly" />
A simple textarea, not even hooked
into the backend server data model.
Then in our javascript (for the demo,
in a separately loaded file, though it
could just as easily be in page) we
have:
var statusUpdate = function statusUpdate(data) {
var statusArea = document.getElementById("statusArea");
var text = statusArea.value;
text = text + "Name: "+data.source.id;
if (data.type === "event") {
text = text +" Event: "+data.name+"\n";
} else { // otherwise, it's an error
text = text + " Error: "+data.name+"\n";
}
statusArea.value = text;
};
// Setup the statusUpdate function to hear all events on the page
jsf.ajax.addOnEvent(statusUpdate);
jsf.ajax.addOnError(statusUpdate);
You could it as well to redirect to an error page using JS.
window.location = '/errors/500.xhtml';

Resources