grails-core icon indicating copy to clipboard operation
grails-core copied to clipboard

grails 5.1.2: deploy plugin war: HTTP Status 404 The requested resource is not available

Open chrisbitmead opened this issue 2 years ago • 8 comments

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

chrisbitmead avatar Feb 08 '22 23:02 chrisbitmead

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.

chrisbitmead avatar Feb 09 '22 07:02 chrisbitmead

Could you please provide an example application? I would like to try and figure out what has changed?

puneetbehl avatar Feb 09 '22 09:02 puneetbehl

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.

chrisbitmead avatar Feb 09 '22 09:02 chrisbitmead

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.

chrisbitmead avatar Feb 10 '22 03:02 chrisbitmead

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.

chrisbitmead avatar Feb 16 '22 00:02 chrisbitmead

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.

osscontributor avatar Feb 23 '22 18:02 osscontributor

@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.

chrisbitmead avatar Feb 23 '22 22:02 chrisbitmead

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.

xpusostomos avatar Aug 29 '23 04:08 xpusostomos