简体   繁体   中英

Using Gradle to package dependencies in a WAR, while leaving transient dependencies in the EAR

I am very new Gradle. I just started looking at it a couple of days ago, and I really like it, so I am trying to convert my project to Gradle.

I just ran into my first difficult problem. I can't figure out how to prevent the 'war' plugin from duplicating a lot of the transient dependencies already found in my EAR. For example, let's say that I want to use org.springframework:spring-web in my WAR. This jar has a lot of transient dependencies, which all get packaged in WEB-INF/lib . This is a problem, b/c all of these dependencies are already in APP-INF/lib of my EAR, b/c there are other modules that use them.

What I'd like to do is to tell Gradle that I wish to compile the WAR code with all the dependencies as per usual, but when it comes to packaging the WAR, I'd like to have some control over what goes into WEB-INF/lib . As far as I can tell, this functionality is not there. Or am I missing something?

I have solved the problem with a hack for now, but I feel that there must be an easier way to do this. I am hoping that someone with more experience can point out where I went wrong here.

This is what I have so far:

apply plugin: 'war'


configurations {
  // Use this config to indicate that a module should be packaged inside the WAR w/o its transitive dependencies
  //
  packageWithoutDependencies 

  // Use this one when you want to package this module and all of its dependencies in the WAR
  //
  packageWithDependencies 

  compile.extendsFrom packageWithoutDependencies
  compile.extendsFrom packageWithDependencies
}

dependencies {
  packageWithDependencies "org.richfaces:richfaces-bom:$richFacesVersion"
  packageWithDependencies "org.richfaces.ui:richfaces-components-ui:$richFacesVersion"
  packageWithDependencies "org.richfaces.core:richfaces-core-impl:$richFacesVersion"

  packageWithoutDependencies "org.springframework:spring-web:$springVersion"
  packageWithoutDependencies "org.springframework.security:spring-security-config:$springSecurityVersion"
  packageWithoutDependencies "org.springframework.security:spring-security-taglibs:$springSecurityVersion"
  packageWithoutDependencies "org.springframework.security:spring-security-web:$springSecurityVersion"
}


// The approach here is to directly modify the classpath used by the 'war' task. This is necessary
// b/c we want to compile with all the dependencies resolved as per usual, yet when we deploy,
// we only want the jars that belong in the WAR, with most transitive dependencies for those already
// deployed as 3rd party jars in the EAR. 
//
task modifyWarClasspath << {
  // Get the names of all non-transitive modules we are packaging
  //
  println " ========= WAR Modules w/o dependencies ========= "
  def firstLevelDeps = configurations.packageWithoutDependencies.resolvedConfiguration.firstLevelModuleDependencies
  def nonTransientDepNames = firstLevelDeps.collect { it.moduleName }
  println nonTransientDepNames

  // Get the names of all direct file includes we are packaging
  //
  println "\n ========= WAR Files w/o dependencies ========= "
  def directFileIncludes = configurations.packageWithoutDependencies
    .getDependencies().findAll{it.hasProperty('source')}
    .source.files*.name.flatten()
  println directFileIncludes

  // Get the names of all transitive modules we a re packaging
  //
  println "\n ========= WAR Modules with dependencies ========= "
  def recursiveDeps = configurations.packageWithDependencies.resolvedConfiguration
    .firstLevelModuleDependencies*.allModuleArtifacts.flatten()

  def transientDepNames = recursiveDeps.collect { it.name }
  println transientDepNames

  // Look through the WAR classpath (runtime - providedCompile), and keep only those
  // files that match a module name on either of the 3 lists we made above.
  //
  println "\n ========= Packaging jars in the WAR ========= "
  war.classpath = war.classpath.findAll {file -> 
    ((nonTransientDepNames + transientDepNames + directFileIncludes + 'main').find {directDepName -> 
      file.name.startsWith(directDepName) 
    } != null)
  }
  war.classpath*.name.sort().each { println it }
  println "\n\n"
}

// Enable 'packageWithDependencies' and 'packageWithoutDependencies' configs
war {
  dependsOn modifyWarClasspath
}

I have a nice simple solution to this now. The key was to make use of a 'provided' scope, and then use it in my WAR dependencies to reference my EAR dependencies. That way the entire WAR classpath has the classpath reachable from the EAR dependencies subtracted from it, so only the web dependencies get packaged in the WAR. Nice and simple.

Here's how:

configurations {
    provided {
        dependencies.all { dep ->
            configurations.default.exclude(group: dep.group, module: dep.name)
        }
    }
    compile.extendsFrom provided
}

And then in your WAR project, you can do:

dependencies {
  provided project(':DFIPConfig')
  provided project(':DFIPSAPBatch')
  provided project(':DFIPDomain')
  // etc...

  compile project(':DryDockProjects:DryDockWebProjects:CommonWebWCAGJar')
  compile project(':DryDockProjects:DryDockWebProjects:CommonParticipantWeb')
  // etc...

  // Other WAR dependencies as per usual

}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM