Issues with Deploying Dynamic Web Project in Eclipse Tomcat

Please bear with the long post as I want to put as much details as I can to get the right help.

I am using Java8, Eclipse Photon, Gradle 5.2 and Tomcat 8.5 as my development environment.

We have a legacy web app with Ant Build which I am trying to switch on to Gradle.

I started with a multi-project setup where my root project MyRootProj contains the following settings.gradle and build.gradle

// MyRootProj build.gradle

allprojects {
    repositories {
        mavenCentral()
    }

    apply plugin : 'eclipse-wtp' 
    apply plugin : 'java'
}

configure(subprojects.findAll {it.name != 'MyWebProj'}) {
    dependencies {
        // dependencies common to all projects go here
        testImplementation 'junit:junit:4.12'
    }

    sourceSets {
        main {
            java {
                srcDirs 'src'
            }
            resources {
                srcDirs 'src'
            }
        }
        
        test {
            java {
                srcDirs 'test'
            }
            resources {
                srcDirs 'test'
            }
        }
    }
}

and settings.gradle

// MyRootProj settings.gradle
rootProject.name = 'MyApp'
include ':JavaProjA', ':JavaProjB', ':JavaProjC', ':MyWebProj'

JavaProjB is dependent on JavaProjA and JavaProjC is dependent on both JavaProjA and JavaProjB.

Individual build.gradle for all four projects are as below:

// JavaProjA build.gradle
dependencies {
   // all dependencies for this project go here
   implementation 'com.google.guava:guava:19.0'
}

for JavaProjB

// JavaProjB build.gradle
dependencies {
   // all dependencies for this project go here
   implementation project(:JavaProjA)
   implementation 'org.apache.poi:poi:3.10-FINAL'
}

for JavaProjC

// JavaProjC build.gradle
dependencies {
   // all dependencies for this project go here
   implementation project(:JavaProjA)
   implementation project(:JavaProjB)
   implementation 'org.apache.commons:commons-lang3:3.3.2'
}

And finally for MyWebProj

// MyWebProj build.gradle
apply plugin: 'war'
apply plugin: 'eclipse-wtp'

war {
    baseName = 'MyApp'
    webAppDirName = 'src/webapp'
}

// copying required files
tasks.war.dependsOn(/* task to copy required jsp, html, etc. in src/webapp */)

dependencies {
    implementation project(:JavaProjA)
	implementation project(:JavaProjB)
	implementation project(:JavaProjC)
}

// Stop eclipse tying to find java source in web project.
sourceSets {
    main {
        java.srcDirs = []
    }
}

eclipse {
    wtp {
        facet {
            facet name: "jst.web", version: "2.5"  // Legacy app. Can't upgrade
        }
    }
}

At this point I identified two issues and did the following to resolve them:

Issue 1: When I import MyRootProj in Eclipse, I get duplicates on file search. As Eclipse loads all project under MyRootProj as well as all projects as independent projects as well.

To resolve this, I moved to includeFlat. So under a MyRootDir, I now have MyRootProj as a sibling project alongside JavaProjA, JavaProjB, JavaProjC and MyWebProj.

So MyRootProj's settings.gradle has this line now:

includeFlat 'JavaProjA', 'JavaProjB', 'JavaProjC', 'MyWebProj'  // note the absence of : colon

Everything else stayed the same.

Issue 2: Since the implementation lines for JavaProjA were getting duplicated in JavaProjC and MyWebProj and line for JavaProjB in JavaProjC and MyWebProj. I switched from java plugin to java-library plugin so that I can use api.

So allProjects block of MyRootProj's build.gradle now has this line:

apply plugin : 'java-library'  // instead of java to use api

And the dependency lines in JavaProjB, JavaProjC and MyWebProj got changed to:

api project(':JavaProjA')  // in JavaProjB's build.gradle

and

api project(':JavaProjB')  // in JavaProjC's build.gradle

and

api project(':JavaProjC')  // in MyWebProj's build.gradle

All other external dependencies (like apache commons, and google guava) stayed as implementation

So this is my setup right now:

  • includeFlat structure
  • java-library plugin for using api
  • war and eclipse-wtp (along with java-library through allProjects block) plugin in Web Project.

Now my eclipse has latest version of BuildShip installed.

  Buildship: Eclipse Plug-ins for Gradle 
  3.0.1.v20181217-1547 
  org.eclipse.buildship.feature.group	
  Eclipse Buildship

At this point, when I import MyRootProj in eclipse as a Gradle Project; it nicely imports JavaProjA, JavaProjB, JavaProjC, MyWebProj as well alongside MyRootProj.

Duplicates in file search are all gone and build task runs successfully for all projects resulting in a WAR file, which if I drop it directly in Tomcat's webapp directory outside Eclipse and run Tomcat from windows services, runs as expected.

However, our development is all on Eclipse and thus it would be nice if I can run my application in the Tomcat through Eclipse's Server tab. It would also be great if hot deploy can be enabled so that any change to a file in any of the project, automatically gets reflected inside the running app in Tomcat.

But that’s not happening. When I right click on Tomcat on Server tab and do Add and Remove and add MyWebProj and start Tomcat, I see a directory MyWebProj getting created inside wtpwebapps folder of Tomcat directory and it has all the files copied correctly through the tasks.war.dependsOn. But I do not see WEB-INF/lib. And thus the web startup fails complaining about missing classes.

I found the following link https://github.com/gradle/gradle/issues/6341 which shows a workaround to solve this.

So I added the following ONLY IN my MyWebProj's build.gradle:

eclipse {
    wtp {
        component {
        	libConfigurations += [ configurations.runtimeClasspath ]
        }
    }
}

However, that is not making any difference. I still can not run my app and can not see WEB-INF/lib in wtpwebapps

At this point I am stuck and do not know the reason for this happening and would really appreciate any guidance for the same. I am new to Gradle, thus not sure what could be the issue and why the suggested work around is not working.

  • Can it be the flat structure ?
  • Can it be the java-library plugin being used instead of java ?
  • Am I using the work around incorrectly ?
  • Am I doing something else incorrectly ?

My end goal is to make the application run inside eclipse’s tomcat with support of hot deployment for debugging during development.

EDIT 1:

So, the fix shown in that workaround got released today as part of gradle 5.3.1. I upgraded my wrapper to that and every thing started working. I can see WEB-INF/lib now, and my application successfully starts from inside Eclipse’s Tomcat.

The only remaining issue is Hot Deployment.

Say I have my application up and running inside Eclipse on Tomcat which is in Debug Mode.

I add a System.out.println in a java class inside JavaProjectA.

This is when weird things starts happening.

  • Project jars start getting deleted fromWEB-INF/lib of my project’s directory inside wtpwebapps
  • When I try to add a breakpoint and debug and modify code on the fly when the control is paused on breakpoint, it seems new instances of tomcat are spawned.
  • I also get errors about locks on class files.

How do I stabilize hot deploy with the setup described above ?

I don’t know if it can help, but, for sure, you need to apply the eclipse-wtp plugin to all projects your webapp project depends on. See: A dependency project needs to apply eclipse-wtp plugin as well for things to work ok - make it explicit · Issue #1333 · gradle/gradle · GitHub

Mauro