sysgears / grain

Grain is a lightweight and powerful static website generator with custom themes to help create static, SEO-friendly websites or a blog in no time.
https://sysgears.com/grain
Other
160 stars 30 forks source link

NPE when invoking generate #12

Closed chrisime closed 8 years ago

chrisime commented 8 years ago

With my current setup I get an NPE when I'm trying to generate html pages. grain doesn't tell me what's wrong about my configuration and/or urls.

./grainw generate
OpenJDK 64-Bit Server VM warning: ignoring option PermSize=32m; support was removed in 8.0
OpenJDK 64-Bit Server VM warning: ignoring option MaxPermSize=128m; support was removed in 8.0
00:10:19.296 [main] INFO  c.sysgears.grain.init.ToolsExtractor - Grain version: 0.6.6
00:10:19.307 [main] INFO  c.sysgears.grain.init.ToolsExtractor - Using tools home: /home/chrisime/.grain/tools/d9cba5b8bf3d86c6deb58d28073d9fbc
00:10:22.764 [main] INFO  c.s.grain.config.ConfigUpdater - Rereading config /home/chrisime/Devel/business.backup/SiteConfig.groovy
00:10:24.170 [main] INFO  c.s.grain.config.ConfigUpdater - Production environment is used
00:10:24.249 [main] INFO  c.sysgears.grain.config.ConfigBinder - Switching from com.sysgears.grain.rpc.python.AutoPython to com.sysgears.grain.rpc.python.Jython service for python.interpreter
00:10:24.861 [main] INFO  c.sysgears.grain.config.ConfigBinder - Switching from com.sysgears.grain.rpc.ruby.AutoRuby to com.sysgears.grain.rpc.ruby.JRuby service for ruby.interpreter
00:10:24.910 [main] INFO  c.sysgears.grain.config.ConfigBinder - Switching from com.sysgears.grain.css.compass.RubyCompass to com.sysgears.grain.css.compass.FakeCompass service for features.compass
00:10:25.415 [main] INFO  c.s.grain.generate.SiteGenerator - Building resource registry...
00:10:25.453 [Actor Thread 1] INFO  com.sysgears.grain.registry.Registry - Starting resource scan
00:10:26.198 [Actor Thread 1] INFO  com.sysgears.grain.registry.Registry - Finished resource scan
00:10:26.218 [Actor Thread 1] INFO  com.sysgears.grain.registry.Registry - Starting filling up template cache
00:10:28.627 [Actor Thread 1] INFO  com.sysgears.grain.registry.Registry - Finished filling up template cache
00:10:28.633 [main] INFO  c.s.grain.registry.URLRegistry - Rebuilding url->resource map
00:10:28.796 [main] INFO  c.s.grain.generate.SiteGenerator - Generating resources... Time elapsed: 3399
java.lang.NullPointerException: Cannot invoke method endsWith() on null object
    at org.codehaus.groovy.runtime.NullObject.invokeMethod(NullObject.java:77)
    at org.codehaus.groovy.runtime.callsite.PogoMetaClassSite.call(PogoMetaClassSite.java:45)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:45)
    at org.codehaus.groovy.runtime.callsite.NullCallSite.call(NullCallSite.java:32)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:45)
    at java_lang_String$endsWith$8.call(Unknown Source)
    at com.sysgears.grain.generate.SiteGenerator$_generate_closure1.doCall(SiteGenerator.groovy:83)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:90)
    at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:233)
    at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:272)
    at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:909)
    at groovy.lang.Closure.call(Closure.java:423)
    at groovy.lang.Closure.call(Closure.java:439)
    at org.codehaus.groovy.runtime.DefaultGroovyMethods.each(DefaultGroovyMethods.java:1324)
    at org.codehaus.groovy.runtime.DefaultGroovyMethods.each(DefaultGroovyMethods.java:1296)
    at org.codehaus.groovy.runtime.dgm$147.invoke(Unknown Source)
    at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite$PojoMetaMethodSiteNoUnwrapNoCoerce.invoke(PojoMetaMethodSite.java:271)
    at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite.call(PojoMetaMethodSite.java:53)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:45)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:108)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:116)
    at com.sysgears.grain.generate.SiteGenerator.generate(SiteGenerator.groovy:81)
    at com.sysgears.grain.generate.SiteGenerator$generate.call(Unknown Source)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:45)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:108)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:112)
    at com.sysgears.grain.Application.run(Application.groovy:131)
    at com.sysgears.grain.Application$run.call(Unknown Source)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:45)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:108)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:112)
    at com.sysgears.grain.Main.main(Main.groovy:81)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at com.sysgears.grain.SiteLauncher.run(SiteLauncher.java:111)
    at com.sysgears.grain.SiteLauncher.main(SiteLauncher.java:40)
larixer commented 8 years ago

Hi @chrisime

This exception is thrown when url is null on one of your pages. I think it's set to null by ResourceMapper of your theme.

Grain is not very helpful with identifying the resource on which url is null, hence I've added resource location printing when this type of error happens to 0.7.0-SNAPSHOT. Please update your application.properties to have the line: grain.version = 0.7.0-SNAPSHOT

Note, that \n (end of line character) is important at the end of application.properties.

This will help you understand what is the location of resource that has url = null. If everything will be fine I will backport this extended diagnostics into stable Grain version.

Victor

chrisime commented 8 years ago

Hi @vlasenko

Thanks for your reply. Unfortunately, the error still happens in your snapshot version. The url where the NPE is thrown isn't shown. I double checked my urls and can't think of anything which is wrong.

Any ideas?

Cheers, Christian

larixer commented 8 years ago

Hi. The snapshot version was not meant to "fix this error", it will dump extended diagnostic info instead. The error is not in the Grain at all, it's in the theme code. In the stack trace from snapshot version there should be location of the resource with null URL. Please post dumped stack trace here and source code of your ResourceMapper

larixer commented 8 years ago

Closing due to not a Grain issue and inactivity

chrisime commented 8 years ago

Gonna post the problem on the weekend, didn't have time. Please reopen. Thanks!

larixer commented 8 years ago

Okay, thanks for update. Looking forward to hear from you soon

chrisime commented 8 years ago

Hi,

I got the following stacktrace now:

java.lang.RuntimeException: Wrong url 'null' for resource at null
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:422)

The relevant parts of ResourceMapper.groovy is (I modified your business theme):

...

    private def customizeModels = { List resources ->
        def posts = resources.findAll { it.layout == 'unternehmer' }
        def isPageable = true

        resources.inject([]) { List updatedResources, Map page ->
            def applyPagination = { items, perPage, url, model = [:] ->
                isPageable = posts.size() > perPage;
                updatedResources += Paginator.paginate(items, 'posts', perPage, url, page + model)
            }
            switch (page.url) {
                case '/':
                case '/pioniere/':
                    applyPagination(posts, 4, page.url)
                    break
                case ~/${site.pioniere_base_dir}.*/:
                    def post = posts.find { it.url == page.url }
                    def index = posts.indexOf(post)
                    def prev = index > 0 ? posts[index - 1] : null
                    def next = posts[index + 1]
                    updatedResources << (page + [prev_post: prev, next_post: next])
                    break
                default:
                    updatedResources << page
            }

            updatedResources << [pageable: isPageable]
        }
    }

    /**
     * Customize site post URLs
     */
    private def customizeUrls = { Map resource ->
        String location = resource.location
        def update = [:]

        switch (location) {
            case ~/\/pioniere\/posts\/.*/:
                update.url = getPostUrl(site.pioniere_base_dir, location)
                break
        }

        resource + update
    }

...
larixer commented 8 years ago

Hi @chrisime It's difficult for me to track down where the resource with location = null is born. Can you just print the input of your resource mapper and output of your resource mapper and try to track down where the page with location = null is produced? If it's in the input to resource mapper - it's a bug inside Grain, but if it's in the output - then it's a bug inside resource mapper, since either 'location' or 'source'+'markup' property must be set on each resource produced by resource mapper.

    def map = { resources ->
        println 'Input pages with null location: ' + resources.findAll { !it.location }
        ...
        def refinedResources = resources.findResults(filterPublished).collect {
            customizeUrls <<
                fillDates <<
                resource
        }.sort { -it.date.time }

        def result = customizeModels << refinedResources

        println 'Output pages with null location: ' + result.findAll { !it.location }

        result
    }
larixer commented 8 years ago

Please note, that the problem in your code is here:

updatedResources << [pageable: isPageable]

You are adding malformed resource with only one property here: [pageable: true/false] and it's location = null of course, because it's not set. You should add pageable property to existing pages instead

chrisime commented 8 years ago

Hi @vlasenko,

so far 'input pages' doesn't give me any null locations. So it seems to be the output.

How can I get around adding malformed resources. I don't see what's wrong since in each case updatedResources gets the right stuff.

Thanks!

larixer commented 8 years ago

Hi @chrisime,

I've pointed out what's wrong in your code already: https://github.com/sysgears/grain/issues/12#issuecomment-172384602

You are adding malformed resources in the line of your code pointed out in the comment above. Please see Resource representation of Grain documentation: http://sysgears.com/grain/docs/latest/#resource-representation

The Groovy map of invalid resource in your case is: {pageable: false} which is incorrect, because required keys 'location' and 'url' are absent in this map.

chrisime commented 8 years ago

Hello @vlasenko,

thanks for the link, gonna try this. You can close the issue for now.

Cheers, Christian