I have a Spring + JSP application (MyFaces + PrimeFaces) and I've added a filter to it in order to add the same headers to all responses.
The application is working fine but if I restart the server and someone has an open page (with a now invalid or non-existing session) I start getting NullPointerException all the time, but it's happening outside of my code. How can I fix this?
The problem seems to be in org.primefaces.util.ResourceUtils:66 (getComponentResources(FacesContext context)) where it tries this:
List<UIComponent> resources = context.getViewRoot().getComponentResources(context, "head");
But context.getViewRoot() returns null so it fails.
If the client reloads the page, he is redirected to the login page correctly, a new session is created, and errors stop.
NoCacheFilter class:
#WebFilter(servletNames = { "Faces Servlet" })
public class NoCacheFilter implements Filter {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
// Skip JSF resources (CSS/JS/Images/etc)
if (!req.getRequestURI().startsWith(req.getContextPath() + ResourceHandler.RESOURCE_IDENTIFIER)) {
res.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); // HTTP 1.1.
res.setHeader("Pragma", "no-cache"); // HTTP 1.0.
res.setDateHeader("Expires", 0); // Proxies.
}
chain.doFilter(req, res);
}
#Override
public void init(FilterConfig filterConfig) throws ServletException {
}
#Override
public void destroy() {
}
}
Exception Trace:
java.lang.NullPointerException
at org.primefaces.util.ResourceUtils.getComponentResources(ResourceUtils.java:66)
at org.primefaces.context.PrimePartialResponseWriter.startMetadataIfNecessary(PrimePartialResponseWriter.java:280)
at org.primefaces.context.PrimePartialResponseWriter.startError(PrimePartialResponseWriter.java:107)
at org.apache.myfaces.shared.context.AjaxExceptionHandlerImpl.renderAjaxError(AjaxExceptionHandlerImpl.java:274)
at org.apache.myfaces.shared.context.AjaxExceptionHandlerImpl.handle(AjaxExceptionHandlerImpl.java:238)
at javax.faces.context.ExceptionHandlerWrapper.handle(ExceptionHandlerWrapper.java:61)
at org.apache.myfaces.lifecycle.LifecycleImpl.executePhase(LifecycleImpl.java:217)
at org.apache.myfaces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:143)
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:198)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:292)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
at eu.calabacin.controller.NoCacheFilter.doFilter(NoCacheFilter.java:30)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
at eu.calabacin.controller.GlobalFilter.doFilter(GlobalFilter.java:31)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:212)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:141)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:616)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:522)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1095)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:672)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1502)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1458)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:745)
Section of web.xml where GlobalFilter is defined:
<filter>
<filter-name>GlobalFilter</filter-name>
<filter-class>eu.calabacin.controller.GlobalFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>GlobalFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
GlobalFilter class:
public class GlobalFilter implements Filter {
private final static String LOGIN_PAGE = "/" + GlobalService.LOGIN_PAGE + ".xhtml";
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpSession session = ((HttpServletRequest) request).getSession(false);// don't create a session if there isn't one
if (session != null && !session.isNew()) {
chain.doFilter(request, response);
} else {
redirectToLogin(request, response, chain);
}
}
private void redirectToLogin(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
String pathInfo = httpRequest.getServletPath();
if (pathInfo == null || !pathInfo.equalsIgnoreCase(LOGIN_PAGE)) {
String newUrl = httpRequest.getContextPath() + LOGIN_PAGE;
httpResponse.sendRedirect(newUrl);
} else {
chain.doFilter(request, response);
}
}
#Override
public void init(FilterConfig filterConfig) throws ServletException {
}
#Override
public void destroy() {
}
}
Software versions:
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring.version>4.2.0.RELEASE</spring.version>
<myfaces.version>2.2.8</myfaces.version>
<primefaces.version>6.0</primefaces.version>
<hibernate.version>5.0.0.Final</hibernate.version>
</properties>
I guess I could 'fix' the problem by changing the ResourceUtils class checking if context.getViewRoot() returns null before trying to use it, but I'm sure I must be doing something wrong somewhere that causes this to fail.
Anyone knows what I'm doing wrong or how I could fix this?
Thank you.
Today I ran into a similar problem (having a NullPointerException exactly at the same PrimeFaces line).
I solved the problem upgrading from Primefaces 6.0 to Primefaces 6.1 and correcting a wrong JPA query that was causing a previous exception (I'm not completely sure which action solved the problem, but I guess it was the upgrade).
Related
Good Day!
Im having a problem with my log in in JSF, it seems that after the log in, the session becomes null. It logs me in the first, but when i hit refresh or go to another page it redirects to login. Hope anybody can help with this. Below are my codes.
Thanks
Filter Class:
#Override
public void doFilter(ServletRequest arg0, ServletResponse arg1,
FilterChain arg2) throws IOException, ServletException {
// TODO Auto-generated method stub
HttpServletRequest request = (HttpServletRequest) arg0;
HttpSession session = request.getSession();
User user = (session != null) ? (User) session.getAttribute("userBean") : null;
if(user != null){
arg2.doFilter(arg0, arg1);
}else{
HttpServletResponse response = (HttpServletResponse) arg1;
response.sendRedirect(request.getContextPath() + "/login.xhtml");
}
}
Login from User Managed Bean Class:
public void login(){
UserDAO action = new UserDAO();
try{
User u = action.login(username, password);
if(u == null){
FacesContext.getCurrentInstance().getExternalContext().redirect(showLogin() + ".xhtml?ref=err");
}else{
FacesContext.getCurrentInstance().getExternalContext().getSession(true);
FacesContext.getCurrentInstance().getExternalContext().getSessionMap().put("userBean", u);
FacesContext.getCurrentInstance().getExternalContext().redirect("pages/dashboard.xhtml");
}
}catch(Exception e){
e.printStackTrace();
}
}
My web.xml:
<filter>
<filter-name>loginFilter</filter-name>
<filter-class>rpt.filters.LoginFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>loginFilter</filter-name>
<url-pattern>/pages/*</url-pattern>
</filter-mapping>
I have implemented a Servlet Filter to do my system's authorization and where needed I redirect the user using this:
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
...
if(!isLoggedIn(currentUser)){
if(isSecurePage(requestedPage)){
redirectUser(resp,USER_LOGIN_PAGE);
}
else{ // Carry on
chain.doFilter(request,response);
}
}
else{
if(!isSecurePage(requestedPage)){
redirectUser(resp,USER_WELCOME_PAGE);
}
else if(!canAccess(currentUser,requestedPage)){
redirectUser(resp,req.getContextPath() + USER_DENIED_PAGE);
}
else{ // Carry on
chain.doFilter(request,response);
}
}
}
private void redirectUser(HttpServletResponse response, String page) throws IOException {
response.sendRedirect(page);
}
The redirect happens correctly. My issue is that although ACCESS_DENIED_PAGE is displayed, the JSF action method behind the original request is still executed (a FacesMessage that it creates is displayed in the ACCESS_DENIED_PAGE, for example).
How to prevent this?
I'm getting a ViewExpiredException when I'm at my login page, it is not supposed to be. So I read a post (recommended by BalusC) on Filters. It might be that the page is loading from browser's cache and not from server. So I've implemented this code
#WebFilter(servletNames={"Faces Servlet"})
public class NoCacheFilter implements Filter {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpReq = (HttpServletRequest) request;
HttpServletResponse httpRes = (HttpServletResponse) response;
if (!httpReq.getRequestURI().startsWith(httpReq.getContextPath() + ResourceHandler.RESOURCE_IDENTIFIER)) { // Skip JSF resources (CSS/JS/Images/etc)
httpRes.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); // HTTP 1.1.
httpRes.setHeader("Pragma", "no-cache"); // HTTP 1.0.
httpRes.setDateHeader("Expires", 0); // Proxies.
}
chain.doFilter(request, response);
}
// ...
#Override
public void init(FilterConfig filterConfig) throws ServletException {
throw new UnsupportedOperationException("Not supported yet.");
}
#Override
public void destroy() {
throw new UnsupportedOperationException("Not supported yet.");
}
}
but when I try to test it I get the following error:
The module has not been deployed.
at org.netbeans.modules.j2ee.deployment.devmodules.api.Deployment.deploy(Deployment.java:210)
at org.netbeans.modules.j2ee.ant.Deploy.execute(Deploy.java:106)
at org.apache.tools.ant.UnknownElement.execute(UnknownElement.java:291)
at sun.reflect.GeneratedMethodAccessor193.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at org.apache.tools.ant.dispatch.DispatchUtils.execute(DispatchUtils.java:106)
at org.apache.tools.ant.Task.perform(Task.java:348)
at org.apache.tools.ant.Target.execute(Target.java:390)
at org.apache.tools.ant.Target.performTasks(Target.java:411)
at org.apache.tools.ant.Project.executeSortedTargets(Project.java:1399)
at org.apache.tools.ant.Project.executeTarget(Project.java:1368)
at org.apache.tools.ant.helper.DefaultExecutor.executeTargets(DefaultExecutor.java:41)
at org.apache.tools.ant.Project.executeTargets(Project.java:1251)
at org.apache.tools.ant.module.bridge.impl.BridgeImpl.run(BridgeImpl.java:284)
at org.apache.tools.ant.module.run.TargetExecutor.run(TargetExecutor.java:539)
at org.netbeans.core.execution.RunClassThread.run(RunClassThread.java:153)
You're throwing an exception during filter's initialization (and destroy). So filter's initialization will completely fail and cause the webapp's startup to be blocked. Remove those lines. You shouldn't purposefully throw an exception in there.
#Override
public void init(FilterConfig filterConfig) throws ServletException {
// NOOP.
}
#Override
public void destroy() {
// NOOP.
}
Read the filter's javadoc to learn about what those methods are for.
See also:
Our Servlet-Filters tag wiki page
I was wondering if it was possible to redirect users if a certain c:if clausule is true?
<c:if test="#{loginController.authenticated}">
//redirect to index page
</c:if>
Yes it is possible.
But, I would suggest you to apply filter for /login.jsp and in the filter forward to the other page if the user has already logged in.
Here is the example which tells how to do this using filter:
public class LoginPageFilter implements Filter
{
public void init(FilterConfig filterConfig) throws ServletException
{
}
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException
{
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
if(request.getUserPrincipal() != null){ //If user is already authenticated
response.sendRedirect("/index.jsp");// or, forward using RequestDispatcher
} else{
filterChain.doFilter(servletRequest, servletResponse);
}
}
public void destroy()
{
}
}
Add this filter enty in the web.xml
<filter>
<filter-name>LoginPageFilter</filter-name>
<filter-class>
com.sample.LoginPageFilter
</filter-class>
<init-param>
<param-name>test-param</param-name>
<param-value>This parameter is for testing.</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>LoginPageFilter</filter-name>
<url-pattern>/login.jsp</url-pattern>
</filter-mapping>
Apart from the Filter approach, you can also use <f:event type="preRenderView">. Put this somewhere in top of the view:
<f:event type="preRenderView" listener="#{loginController.checkAuthentication}" />
And add this listener method to the LoginController:
public void checkAuthentication() throws IOException {
ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
if (externalContext.getUserPrincipal() != null) {
externalContext.redirect(externalContext.getRequestContextPath() + "/index.xhtml");
}
}
That's all.
See also:
Is there any easy way to preprocess and redirect GET requests?
I create this filter :
public class LoginFilter implements Filter {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpSession session = req.getSession();
if (session.getAttribute("authenticated") != null || req.getRequestURI().endsWith("login.xhtml")) {
chain.doFilter(request, response);
} else {
HttpServletResponse res = (HttpServletResponse) response;
res.sendRedirect("login.xhtml");
return;
}
}
#Override
public void init(FilterConfig filterConfig) throws ServletException {
}
#Override
public void destroy() {
}
}
This is my structure:
And then I add the filter in the web.xml:
<filter>
<filter-name>LoginFilter</filter-name>
<filter-class>filter.LoginFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>LoginFilter</filter-name>
<servlet-name>Faces Servlet</servlet-name>
</filter-mapping>
The filter works as it should but keeps giving me this error:
"Was not possible find or provider the resource, login"
And after that my richfaces doesn't works anymore.
How can I solve that ? Or create a web filter correctly ?
Any path-relative URL (i.e. URLs which do not start with /) which you pass to sendRedirect() will be relative to the current request URI. I understand that the login page is at http://localhost:8080/contextname/login.xhtml. So, if you for example access http://localhost:8080/contextname/pages/user/some.xhtml, then this redirect call will actually point to http://localhost:8080/contextname/pages/user/login.xhtml, which I think don't exist. Look at the URL in your browser address bar once again.
To fix this problem, rather redirect to a domain-relative URL instead, i.e. start the URL with /.
res.sendRedirect(req.getContextPath() + "/login.xhtml");