Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Grails 3.0.12: Missing layout in view throws NullPointerException #9600

Closed
mhuebner opened this issue Jan 28, 2016 · 1 comment
Closed

Grails 3.0.12: Missing layout in view throws NullPointerException #9600

mhuebner opened this issue Jan 28, 2016 · 1 comment

Comments

@mhuebner
Copy link

This problem exists since Grails 3.0.12 and apparently only if an ErrorController is used for handling error logic.

I attached a project which reproduces this issue.

Just run it and request

http://localhost:8080/not-mapped-uri

This results in a

ERROR org.apache.catalina.core.ContainerBase.[Tomcat].[localhost].[/].[grailsDispatcherServlet] - Servlet.service() for servlet grailsDispatcherServlet threw exception
java.lang.NullPointerException: null
    at org.grails.web.sitemesh.GroovyPageLayoutFinder$LayoutCacheKey.hashCode(GroovyPageLayoutFinder.java:262) ~[grails-web-sitemesh-3.0.12.jar:3.0.12]
    at java.util.concurrent.ConcurrentHashMap.get(ConcurrentHashMap.java:936) ~[na:1.8.0_45]
    at org.grails.web.sitemesh.GroovyPageLayoutFinder.findLayout(GroovyPageLayoutFinder.java:127) ~[grails-web-sitemesh-3.0.12.jar:3.0.12]
    at org.grails.web.sitemesh.GroovyPageLayoutFinder.findLayout(GroovyPageLayoutFinder.java:94) ~[grails-web-sitemesh-3.0.12.jar:3.0.12]
    at org.grails.web.sitemesh.GrailsLayoutView.renderTemplate(GrailsLayoutView.java:65) ~[grails-web-sitemesh-3.0.12.jar:3.0.12]
    at org.grails.web.servlet.view.AbstractGrailsView.renderWithinGrailsWebRequest(AbstractGrailsView.java:71) ~[grails-web-common-3.0.12.jar:3.0.12]
    at org.grails.web.servlet.view.AbstractGrailsView.renderMergedOutputModel(AbstractGrailsView.java:55) ~[grails-web-common-3.0.12.jar:3.0.12]
    at org.springframework.web.servlet.view.AbstractView.render(AbstractView.java:303) ~[spring-webmvc-4.1.8.RELEASE.jar:4.1.8.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1244) ~[spring-webmvc-4.1.8.RELEASE.jar:4.1.8.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1027) ~[spring-webmvc-4.1.8.RELEASE.jar:4.1.8.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:971) ~[spring-webmvc-4.1.8.RELEASE.jar:4.1.8.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:893) ~[spring-webmvc-4.1.8.RELEASE.jar:4.1.8.RELEASE]
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:967) ~[spring-webmvc-4.1.8.RELEASE.jar:4.1.8.RELEASE]
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:858) ~[spring-webmvc-4.1.8.RELEASE.jar:4.1.8.RELEASE]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:622) ~[tomcat-embed-core-8.0.28.jar:8.0.28]
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:843) ~[spring-webmvc-4.1.8.RELEASE.jar:4.1.8.RELEASE]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:729) ~[tomcat-embed-core-8.0.28.jar:8.0.28]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:291) ~[tomcat-embed-core-8.0.28.jar:8.0.28]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) ~[tomcat-embed-core-8.0.28.jar:8.0.28]
    at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:720) [tomcat-embed-core-8.0.28.jar:8.0.28]
    at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:468) [tomcat-embed-core-8.0.28.jar:8.0.28]
    at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:391) [tomcat-embed-core-8.0.28.jar:8.0.28]
    at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:318) [tomcat-embed-core-8.0.28.jar:8.0.28]
    at org.apache.catalina.core.StandardHostValve.custom(StandardHostValve.java:439) [tomcat-embed-core-8.0.28.jar:8.0.28]
    at org.apache.catalina.core.StandardHostValve.status(StandardHostValve.java:305) [tomcat-embed-core-8.0.28.jar:8.0.28]
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:182) [tomcat-embed-core-8.0.28.jar:8.0.28]
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79) [tomcat-embed-core-8.0.28.jar:8.0.28]
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88) [tomcat-embed-core-8.0.28.jar:8.0.28]
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:518) [tomcat-embed-core-8.0.28.jar:8.0.28]
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1091) [tomcat-embed-core-8.0.28.jar:8.0.28]
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:673) [tomcat-embed-core-8.0.28.jar:8.0.28]
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1500) [tomcat-embed-core-8.0.28.jar:8.0.28]
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1456) [tomcat-embed-core-8.0.28.jar:8.0.28]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [na:1.8.0_45]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [na:1.8.0_45]
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-8.0.28.jar:8.0.28]
    at java.lang.Thread.run(Thread.java:745) [na:1.8.0_45]
ERROR org.apache.catalina.core.ContainerBase.[Tomcat].[localhost] - Exception Processing ErrorPage[errorCode=404, location=/error]
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.NullPointerException
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:979) ~[spring-webmvc-4.1.8.RELEASE.jar:4.1.8.RELEASE]
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:858) ~[spring-webmvc-4.1.8.RELEASE.jar:4.1.8.RELEASE]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:622) ~[tomcat-embed-core-8.0.28.jar:8.0.28]
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:843) ~[spring-webmvc-4.1.8.RELEASE.jar:4.1.8.RELEASE]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:729) ~[tomcat-embed-core-8.0.28.jar:8.0.28]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:291) ~[tomcat-embed-core-8.0.28.jar:8.0.28]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) ~[tomcat-embed-core-8.0.28.jar:8.0.28]
    at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:720) ~[tomcat-embed-core-8.0.28.jar:8.0.28]
    at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:468) ~[tomcat-embed-core-8.0.28.jar:8.0.28]
    at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:391) ~[tomcat-embed-core-8.0.28.jar:8.0.28]
    at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:318) ~[tomcat-embed-core-8.0.28.jar:8.0.28]
    at org.apache.catalina.core.StandardHostValve.custom(StandardHostValve.java:439) [tomcat-embed-core-8.0.28.jar:8.0.28]
    at org.apache.catalina.core.StandardHostValve.status(StandardHostValve.java:305) [tomcat-embed-core-8.0.28.jar:8.0.28]
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:182) [tomcat-embed-core-8.0.28.jar:8.0.28]
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79) [tomcat-embed-core-8.0.28.jar:8.0.28]
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88) [tomcat-embed-core-8.0.28.jar:8.0.28]
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:518) [tomcat-embed-core-8.0.28.jar:8.0.28]
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1091) [tomcat-embed-core-8.0.28.jar:8.0.28]
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:673) [tomcat-embed-core-8.0.28.jar:8.0.28]
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1500) [tomcat-embed-core-8.0.28.jar:8.0.28]
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1456) [tomcat-embed-core-8.0.28.jar:8.0.28]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [na:1.8.0_45]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [na:1.8.0_45]
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-8.0.28.jar:8.0.28]
    at java.lang.Thread.run(Thread.java:745) [na:1.8.0_45]
Caused by: java.lang.NullPointerException: null
    at org.grails.web.sitemesh.GroovyPageLayoutFinder$LayoutCacheKey.hashCode(GroovyPageLayoutFinder.java:262) ~[grails-web-sitemesh-3.0.12.jar:3.0.12]
    at java.util.concurrent.ConcurrentHashMap.get(ConcurrentHashMap.java:936) ~[na:1.8.0_45]
    at org.grails.web.sitemesh.GroovyPageLayoutFinder.findLayout(GroovyPageLayoutFinder.java:127) ~[grails-web-sitemesh-3.0.12.jar:3.0.12]
    at org.grails.web.sitemesh.GroovyPageLayoutFinder.findLayout(GroovyPageLayoutFinder.java:94) ~[grails-web-sitemesh-3.0.12.jar:3.0.12]
    at org.grails.web.sitemesh.GrailsLayoutView.renderTemplate(GrailsLayoutView.java:65) ~[grails-web-sitemesh-3.0.12.jar:3.0.12]
    at org.grails.web.servlet.view.AbstractGrailsView.renderWithinGrailsWebRequest(AbstractGrailsView.java:71) ~[grails-web-common-3.0.12.jar:3.0.12]
    at org.grails.web.servlet.view.AbstractGrailsView.renderMergedOutputModel(AbstractGrailsView.java:55) ~[grails-web-common-3.0.12.jar:3.0.12]
    at org.springframework.web.servlet.view.AbstractView.render(AbstractView.java:303) ~[spring-webmvc-4.1.8.RELEASE.jar:4.1.8.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1244) ~[spring-webmvc-4.1.8.RELEASE.jar:4.1.8.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1027) ~[spring-webmvc-4.1.8.RELEASE.jar:4.1.8.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:971) ~[spring-webmvc-4.1.8.RELEASE.jar:4.1.8.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:893) ~[spring-webmvc-4.1.8.RELEASE.jar:4.1.8.RELEASE]
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:967) ~[spring-webmvc-4.1.8.RELEASE.jar:4.1.8.RELEASE]
    ... 24 common frames omitted

The main problem here is that this happens only if in production mode OR groovyPageLayoutFinder.setCacheEnabled(true) (the latter is used to "simulate" production behaviour in Bootstrap.groovy).

The view which should be rendered via ErrorController.notfound contains a
<g:applyLayout name="main">...<g:applyLayout>

You could also remove this line completely which will end up in the same error.

BUT if you add a layout via

<meta name="layout" content="main">

the app behaves as expected.

What I can see when debugging the app is: GroovyPageLayoutFinder.findLayout just resolves page.getProperty("meta.layout").

public Decorator findLayout(HttpServletRequest request, Page page) {
        if (LOG.isDebugEnabled()) {
            LOG.debug("Evaluating layout for request: " + request.getRequestURI());
        }
        final Object layoutAttribute = request.getAttribute(LAYOUT_ATTRIBUTE);
        if (request.getAttribute(RENDERING_VIEW_ATTRIBUTE) != null || layoutAttribute != null) {
            String layoutName = layoutAttribute == null ? null : layoutAttribute.toString();

            if (layoutName == null) {
                layoutName = page.getProperty("meta.layout");
            }

            Decorator d = null;

            if (GrailsStringUtils.isBlank(layoutName)) {
                GroovyObject controller = (GroovyObject)request.getAttribute(GrailsApplicationAttributes.CONTROLLER);
                if (controller != null) {
                    String controllerName = (String)controller.getProperty(ControllerDynamicMethods.CONTROLLER_NAME_PROPERTY);
                    String actionUri = (String)controller.getProperty(ControllerDynamicMethods.ACTION_URI_PROPERTY);

                    if (LOG.isDebugEnabled()) {
                        LOG.debug("Found controller in request, location layout for controller [" + controllerName
                                + "] and action [" + actionUri + "]");
                    }

                    LayoutCacheKey cacheKey = null;
                    boolean cachedIsNull = false;

                    if (cacheEnabled) {
                        cacheKey = new LayoutCacheKey(controllerName, actionUri);
                        DecoratorCacheValue cacheValue = layoutDecoratorCache.get(cacheKey);
                        if (cacheValue != null && (!gspReloadEnabled || !cacheValue.isExpired())) {
                            d = cacheValue.getDecorator();
                            if (d == null) {
                                cachedIsNull = true;
                            }
                        }
                    }

                    if (d == null && !cachedIsNull) {
                        d = resolveDecorator(request, controller, controllerName, actionUri);
                        if (cacheEnabled) {
                            layoutDecoratorCache.put(cacheKey, new DecoratorCacheValue(d));
                        }
                    }
                }
                else {
                    d = getApplicationDefaultDecorator(request);
                }
            }
            else {
                d = getNamedDecorator(request, layoutName);
            }

            if (d != null) {
                return d;
            }
        }
        return null;
    }

...and controllerName is also null.

@mhuebner
Copy link
Author

Not able to attach a zip which was created by grails bug-report. Creating a repo right now to attach the sample app.

Ok, here it is: https://github.com/mhuebner/grails-issue-9600

mhuebner added a commit to mhuebner/grails-issue-9600 that referenced this issue Jan 28, 2016
@graemerocher graemerocher added this to the grails-3.0.14 milestone Jan 28, 2016
@graemerocher graemerocher self-assigned this Feb 15, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants