grails-core
grails-core copied to clipboard
grails 5.1.2: deploy plugin war: HTTP Status 404 The requested resource is not available
Expected Behavior
I have a number of plugins that are also stand alone apps. i.e. they have a web user interface, but they also provide services (domain classes) to other grails apps as a plugin. This was all working fine in grails 4. It also works ok in grails 5.1.2 when using grails run-app
. Where it has now stopped working when upgrading from grails 4 -> grails 5.1.2 is when deploying a war.
Actual Behaviour
HTTP Status 404 – Not Found
Type Status Report
Message The requested resource [/myPlugin512-0.1/] is not available
Description The origin server did not find a current representation for the target resource or is not willing to disclose that one exists.
There is absolutely nothing in the way of errors under tomcat. Which was extremely confusing for a long time. It happily deploys the war, but then nothing happens.
Steps To Reproduce
sdk default grails 5.1.2
grails create-plugin myPlugin512
cd myPlugin512
vi build.gradle
Now add the ability to create a war:
apply plugin:"war"
Now build:
./gradlew bootWar
cp build/libs/myPlugin512-0.1.war /c/Programs/Tomcat9/webapps
Now try and access your app:
http://localhost:8080/myPlugin512-0.1/
Also, I don't think this is related to other Tomcat issues reported here. I tried the providedRuntime "org.springframework.boot:spring-boot-starter-tomcat"
thing, but it didn't help.
Environment Information
grails 5.1.2 java zulu 13.0.5-fx Windows 11 Tomcat 9.0.56
Example Application
No response
Version
5.1.2
After days of staring at code, trying, retrying and re-retrying, it seems that what is required to get it to work, is to apply more plugins from the app template (this isn't new, and is kinda expected),
apply plugin:"eclipse"
apply plugin:"idea"
apply plugin:"war"
apply plugin:"org.grails.grails-web"
apply plugin:"com.github.erdi.webdriver-binaries"
apply plugin:"org.grails.grails-gsp"
apply plugin:"com.bertramlabs.asset-pipeline"
but ALSO... to remove the annotation from Application.groovy:
//@PluginSource // This has to be removed.
class Application extends GrailsAutoConfiguration {
static void main(String[] args) {
GrailsApp.run(Application, args)
}
}
As far as I can tell, this is new behavior. Nothing in the documentation says that this annotation has any functionality, and as far I noticed in grails 4, it never did have any functionality. Now it will stop the war starting in Tomcat, for some reason.
Could you please provide an example application? I would like to try and figure out what has changed?
The sample application is grails create-plugin myPlugin
then change build.gradle to have these plugins: (aka, just like create-app)
apply plugin:"eclipse"
apply plugin:"idea"
apply plugin:"war"
apply plugin:"org.grails.grails-web"
apply plugin:"org.grails.grails-gsp"
apply plugin:"com.bertramlabs.asset-pipeline"
and change the build script dependencies to: (aka, just like create-app)
buildscript {
repositories {
maven { url "https://repo.grails.org/grails/core" }
}
dependencies {
classpath "org.grails:grails-gradle-plugin:$grailsGradlePluginVersion"
classpath "org.grails.plugins:hibernate5:7.2.1"
classpath "com.bertramlabs.plugins:asset-pipeline-gradle:3.3.4"
classpath "org.grails.plugins:views-gradle:2.2.0"
}
}
do a gradlew bootWar
and deploy it to Tomcat9. And if you want it to actually work, remove @PluginSource
from the Application.groovy.
It's worse than what I thought... whilst removing the @PluginSource
from the plugin's Application.groovy allows it to deploy as a stand alone war, it STOPs any app that uses that plugin from deploying as a stand alone war. If I include that plugin in another grails app I get in catalina.out:
Caused by: java.lang.IllegalStateException: Already associated with parent BeanFactory: io.micronaut.spring.context.factory.MicronautBeanFactory@16b2f282: defining beans []; root of factory hierarchy
Doing some Googling, the only person I can find who saw that error says it was because they had multiple Application.groovy. Which sounds plausible given that I've changed Application.groovy.
https://stackoverflow.com/questions/69399368/micronaut-already-associated-with-parent-beanfactory-error-running-grails-4-app
However, in my build.gradle (both plugin's
springBoot { mainClass = 'myplugin.Application' }
and top level), I specify the main Application, just to be sure:
springBoot { mainClass = 'myapp.Application' }
So it doesn't really have any excuse if it's getting confused about which Application is the top level.
And in all cases, it works fine when deployed as standalone
grails run-app
It's only when deployed in tomcat that it's an issue.
The only way I've found to get around this is to create another application, basically an empty shell of a grails app, and import the plugin. That way, the application can have a different Application.groovy to the plugin, without the @Plugin annotation. The smallest app I've been able to make is to have its own build.gradle, gradle.properties, Application.groovy, application.yml, logback.xml. I think surely there must be a way to make a grails app have a deployable standalone war, and also function as a plugin, but I can't figure it out.
I have a number of plugins that are also stand alone apps.
Does that mean that you have a Grails plugin project which also includes a main
method which will startup the embedded Tomcat and configure the app in it? It isn't clear why you want to deploy that but if that is what you are doing, I think using an app will make more sense.
@jeffbrown No, by standalone I mean that it's a grails app that should be able to be built into a war and run in Tomcat. i.e. it's not merely a plugin.
I tried another approach to solving this without having to make a whole external app:
@PluginSource
class Application extends GrailsAutoConfiguration {
static void main(String[] args) {
GrailsApp.run(Application, args)
}
}
class TopApplication extends GrailsAutoConfiguration {
static void main(String[] args) {
GrailsApp.run(Application, args)
}
}
Then I had 2 separate build files, one for the plugin:
springBoot { mainClass = 'au.gov.environment.ibis.auth.Application' }
and one for the war:
springBoot { mainClass = 'au.gov.environment.ibis.auth.TopApplication' }
However this doesn't work. There mere presence of two classes that extend GrailsAutoConfiguration seems to make the plugin non operable when deployed in Tomcat.
And again... none of these are an issue when running outside Tomcat. For some reason, grails doesn't work the same in and out of Tomcat. In a way that's concerning also because is whatever bootstrap process that makes it fail or not fail working correctly in and not in Tomcat?
The only place I can see the annotation used is in BootInitializerClassInjector in grails-web, but I don't know enough about that code to understand why that stops it working.