grails / grails-core

The Grails Web Application Framework
http://grails.org
Apache License 2.0
2.78k stars 951 forks source link

Inline plugin reloading does not work with Gradle 3 #10741

Open chrisbrookes opened 7 years ago

chrisbrookes commented 7 years ago

Steps to Reproduce

  1. clone https://github.com/graemerocher/grails-multi-project-build-example
  2. Set grails to use gradle version 3.5.1: export GRAILS_GRADLE_HOME=/home/user/.sdkman/candidates/gradle/3.5.1
  3. run grails run-app in myapp dir.
  4. Change myplugin/grails-app/controllers/myplugin/FooController.groovy and check to see if it has been reloaded.

Expected Behaviour

FooController should be reloaded.

Actual Behaviour

It isn't reloaded.

Environment Information

Example Application

It does work with Gradle 2.12 (and presumably other 2.x versions):

$ export GRAILS_GRADLE_HOME=/home/chris/.sdkman/candidates/gradle/2.12
$ grails run-app
| Error Error occurred loading commands: org/hibernate/tool/schema/TargetType (Use --stacktrace to see the full trace)
| Error Error occurred loading commands: Could not initialize class grails.dev.commands.ApplicationContextCommandRegistry (Use --stacktrace to see the full trace)
| Running application...
Grails application running at http://localhost:8080 in environment: development
File /home/chris/dev/projects/research/grails3-re/grails-multi-project-build-example/myplugin/grails-app/controllers/myplugin/FooController.groovy changed, recompiling...

Also tried Gradle 4 and this also does not reload. We have a large multi project app that our Grails apps are part of, which requires Gradle 3.x, so we cannot go back to Gradle 2. Have not tried other Gradle 3.x versions.

osscontributor commented 7 years ago

Do you know if it works if you use the dependency syntax described in the Hot Reloading section at http://guides.grails.org/grails-multi-project-build/guide/index.html?

grails {
    plugins {
        compile project(':myplugin')
    }
}

Feedback is welcome. Thank you!

chrisbrookes commented 7 years ago

I did try that and it didn't make a difference initially. But just now I have tried it again and also commented out the compile project(":myplugin") line in the dependencies block and now it does appear to work.

Unhappily, in my actual app, I get the following:

Configuring Spring Security Core ...
... finished configuring Spring Security Core

Grails application running at http://localhost:8080 in environment: development
File /home/chris/dev/projects/re-trunk/amp/web/grails-test-plugin/grails-app/controllers/com/retailexp/web/test/plugin/PluginTestController.groovy changed, recompiling...
Spring Loaded: Cannot reload new version of com.retailexp.web.test.plugin.PluginTestController
 Reason: Interfaces changed 
from [grails/artefact/Controller, grails/artefact/AsyncController, grails/artefact/gsp/TagLibraryInvoker, grails/artefact/controller/RestResponder, grails/artefact/controller/RestResponder$Trait$FieldHelper, grails/artefact/gsp/TagLibraryInvoker$Trait$FieldHelper, grails/web/api/WebAttributes$Trait$FieldHelper, grails/events/Events$Trait$FieldHelper, grails/artefact/Controller$Trait$FieldHelper, grails/web/api/ServletAttributes$Trait$FieldHelper, grails/artefact/controller/support/RequestForwarder$Trait$FieldHelper, grails/artefact/controller/support/ResponseRedirector$Trait$FieldHelper, grails/artefact/controller/support/ResponseRenderer$Trait$FieldHelper, groovy/lang/GroovyObject] 
to [grails/artefact/Controller, grails/artefact/AsyncController, grails/artefact/gsp/TagLibraryInvoker, grails/artefact/controller/RestResponder, grails/plugin/springsecurity/ControllerMixin, grails/artefact/controller/RestResponder$Trait$FieldHelper, grails/artefact/gsp/TagLibraryInvoker$Trait$FieldHelper, grails/web/api/WebAttributes$Trait$FieldHelper, grails/events/Events$Trait$FieldHelper, grails/artefact/Controller$Trait$FieldHelper, grails/web/api/ServletAttributes$Trait$FieldHelper, grails/artefact/controller/support/RequestForwarder$Trait$FieldHelper, grails/artefact/controller/support/ResponseRedirector$Trait$FieldHelper, grails/artefact/controller/support/ResponseRenderer$Trait$FieldHelper, groovy/lang/GroovyObject]

So it's trying to reload at least. Reloading in the actual grails app that is importing the plugin works fine. There's more dependencies coming through from the rest of my multi-project application from other Gradle (Java & Groovy) projects, but looking at a dependency report of my grails app, the versions of Spring (incl springloaded - 1.2.7.RELEASE), Groovy, are still the same as Graeme's reference example app. Any pointers or things I could try to diagnose?

Thanks.

chrisbrookes commented 7 years ago

I noticed the 'to' list of interfaces had an extra one: grails/plugin/springsecurity/ControllerMixin presumably because I'm using the Spring Security Grails plugin (org.grails.plugins:spring-security-core:3.1.2) in my main Grails app - note: not in the plugin. If I take this out of the main Grails app, the reloading works.

I've just reproduced this in Graeme's reference example app too:

  1. In myapp/build.gradle, remove/comment compile project(":myplugin") in dependencies
  2. Add plugin block for inline plugin:
    grails {
    plugins {
        compile project(':myplugin')
    }
    }
  3. Add dependency for Spring Security plugin in dependencies: compile ('org.grails.plugins:spring-security-core:3.1.2')
  4. grails run-app and test the reloading of FooController again.

It does work if you add the Spring Security dependency in the myplugin/build.gradle too, but this isn't something I want to impose on all our Grails plugins. Not sure if this is specific to Spring Security plugin or whether other Grails plugins would also exhibit this problem (I guess it depends on if they add traits to Controllers).

longwa commented 6 years ago

I'm seeing this problem as well with Grails 3.3.2 except I don't even get a hint of reloading. I followed the steps at http://guides.grails.org/grails-multi-project-build/guide/index.html but still not getting any recompile on the plugin side.

I'll test the sample application again to see if it works out of the box but I seem to recall that it does. I suspect there's some configuration or plugin that could be causing issues in certain configurations.

longwa commented 6 years ago

I've been playing around with this and found a couple of things that are causing this to seem so random:

  1. Running with gradle bootRun instead of grails run-app includes the inline plugin classes as a .jar file, preventing reloading from working for the plugin module (it will work for application classes). Maybe this is expected behavior, but I always assumed these commands are the same. This behavior is the same whether you use the gradle or grails method of specifying the dependency.

  2. This one is a weird but, for me, the behavior is different depending on whether the Gradle Daemon was started via the command line or started by IntelliJ. If I run gradle tasks in Intellij; it will start a daemon. If the daemon is forked by IntelliJ; even when I run grails run-app via the command line it includes the .jar file instead of the build path (same as item 1 above). If I kill all the Gradle Daemons and then start one via the command line (by running a command or just running grails run-app, reloading works fine).

ilopmar commented 6 years ago

After discussing this, Graeme suggested to take a look at https://github.com/grails/grails-core/blob/master/grails-gradle-plugin/src/main/groovy/org/grails/gradle/plugin/core/PluginDefiner.groovy#L57

I've been debugging that and Environment.isDevelopmentRun() is returning false. This is what I've found:

Finally I've done a test hard-coding return true in Environment.isDevelopmentRun() and the reloading is working again when starting the application using gradle.

ilopmar commented 6 years ago

Just to add a few more information about why Environment.isDevelopmentRun() is returning false:

mrussmann commented 6 years ago

Hi, I am running into the same problem. I have to stop the gradle daemons manually from time to time to enable recompiling again but then I am getting this error message:

TestController.groovy changed, recompiling...

Spring Loaded: Cannot reload new version of plugin.shoppingcard.TestController Reason: Interfaces changed from [grails/artefact/gsp/TagLibraryInvoker, grails/artefact/Controller, grails/artefact/controller/RestResponder, org/grails/compiler/web/converters/RenderConverterTrait, grails/artefact/controller/RestResponder$Trait$FieldHelper, grails/artefact/Controller$Trait$FieldHelper, grails/web/api/ServletAttributes$Trait$FieldHelper, grails/web/api/WebAttributes$Trait$FieldHelper, grails/artefact/controller/support/RequestForwarder$Trait$FieldHelper, grails/artefact/controller/support/ResponseRedirector$Trait$FieldHelper, grails/artefact/controller/support/ResponseRenderer$Trait$FieldHelper, grails/artefact/gsp/TagLibraryInvoker$Trait$FieldHelper, groovy/lang/GroovyObject] to [grails/artefact/gsp/TagLibraryInvoker, grails/artefact/Controller, grails/artefact/controller/RestResponder, grails/plugin/springsecurity/ControllerMixin, grails/plugins/cacheheaders/CacheHeadersTrait, grails/plugins/mail/SendMail, org/grails/compiler/web/converters/RenderConverterTrait, grails/plugins/mail/SendMail$Trait$FieldHelper, grails/plugins/cacheheaders/CacheHeadersTrait$Trait$FieldHelper, grails/web/api/ServletAttributes$Trait$FieldHelper, grails/web/api/WebAttributes$Trait$FieldHelper, grails/artefact/controller/RestResponder$Trait$FieldHelper, grails/artefact/Controller$Trait$FieldHelper, grails/artefact/controller/support/RequestForwarder$Trait$FieldHelper, grails/artefact/controller/support/ResponseRedirector$Trait$FieldHelper, grails/artefact/controller/support/ResponseRenderer$Trait$FieldHelper, grails/artefact/gsp/TagLibraryInvoker$Trait$FieldHelper, groovy/lang/GroovyObject]

Any idea? Thanks Martin

chrisbrookes commented 6 years ago

@mrussmann See my comment above and the one after, where I get essentially the same error. Looks like it could be an issue with Spring Security and or controller traits.

mrussmann commented 6 years ago

I think that there are two different problems, one is that the recompile on changes does not work and the second is the problem shown above. I even can get a blank project to identify changes in the plugin sources.

chrisbrookes commented 6 years ago

Agree it looks like two different issues. Did you try the workaround (adding the Spring security dependency in the plugin(s) build.gradle)?

mrussmann commented 6 years ago

Hi @chrisbrookes, thanks for the tip, after some debugging I ended up with having the same dependencies for the app and the plugins (even if they are not needed in the plugin) and stopping the gradle daemons manually after changes to the build-file (and I also upgraded to gradle 4.5). Now it seems to work so far.

longwa commented 6 years ago

@ilopmar Nice summary. Looks like @jameskleeh has fixed the issues with BASE_DIR in commit https://github.com/grails/grails-core/commit/75eec8044f483d138f2687f5eba425fafe02bc28

You mentioned that grails.run.active is also wrong but I wasn't sure exactly what you meant. If we can fix that too for 3.3.3, we might be good.

longwa commented 6 years ago

I'm not even sure why the RUN_ACTIVE is there at all. Is there some reason we can't have reloading when running gradle bootRun instead of grails run-app?

I'd prefer to just use the gradle commands as they are supported more naturally by IntelliJ and also seem to start up a little quicker.

Running in 3.3.3.BUILD-SNAPSHOT with some printlns, @ilopmar is right, only RUN_ACTIVE is the problem now:

GRAILS_APP_DIR_PRESENT: true
isStandaloneDeployed: false
isWarDeployed: false
isDevelopmentEnvironmentAvailable: true
RUN_ACTIVE: false
env: DEVELOPMENT

If I hard code to return true, it works even for gradle bootRun.

longwa commented 6 years ago

Ok, this is all coming together now....

Since grails.run.active is a JVM arg to the gradle daemon itself and not bootRun (that's the part I wasn't considering), you'd have to start the daemon with it and it causes issues in other ways to have an exploded jar file.

I added it to gradle.properties and reloading does work with 3.3.3, but it broke gradle assemble when it tried to compile assets, presumably because of the exploded classpath.

Probably the best option is to just run gradle bootRun -Dgrails.run.active=true and setup IntelliJ to also add that JVM arg when executing bootRun. That should allow reloading to work once 3.3.3 is availab.e

bruno-lopes commented 5 years ago

@longwa Does it work with Grails 3.3.9? I could not make hot reload work with gradle bootRun.

supsup commented 5 years ago

@longwa any update if this made it into 3.3.3+?