Gradle: how to exclude jar from an ear file? - gradle

I have a question regarding excluding a jar file from an ear artifact. My project has the following modules:
project
- ejb_module
- war_module
- ear_module
- other_module
In my ear_module/build.gradle :
dependencies {
deploy project(ejb_module)
earlib project(ejb_module)
deploy project(war_module)
earlib project(war_module)
}
The problem is how I exclude the jar artifact produced from the war_module.
The war_module produces both war and jar artifact. The reason I need the jar artifact is that my other_module depends on it.
A complicated factor is that the war_module has some dependencies that needs to go via earlib.
So my question is how I exclude the war_module.jar from the ear file? It is included both at root and in APP-INF/lib.

I presume in your war_module you are applying both war and java plugins to get a .war. and .jar artifacts ?
Try:
deploy project(path: 'war_module', configuration: 'archives')
earlib project(path: 'war_module', configuration: 'archives')
It should pick up a .war only because if look into the Gradle war plugin code you can see that it overrides the default output of the project, if java plugin was included.
Additionally, war plugin uses java plugin to compile your code under the hood.
You, of course, can simply exclude the artefact from the build by using
config (project){exclude module: 'name'}
but I think this is not the best you can do here.
Try looking into providedRuntime and runtimeOnly configurations depending on which version of Gradle do you use.
If the artefact is used for compilation then compileOnly etc.
This configuration used in dependecies{} will use the jar but will not be going to expose it anywhere else then it should not land in any artefact that you build and this is probably what would fit you the most in your case, but the question wasn't asking about this it's just an advice.

Assume your EAR contains an another existing jar, said xxx.yyy.zzz.jar.
In your ear project's build.gradle, add the following
ext.replacement = 'your.group:xxx.yyy.zzz:your.version'
configurations.all {
resolutionStrategy {
eachDependency { DependencyResolveDetails details ->
// remove to.be.excluded.jar
if (details.requested.group == 'your.group' &&
details.requested.name == 'to.be.excluded' &&
details.requested.version == 'your.version') {
details.useTarget replacement
}
}
}
}
This way, the to.be.excluded.jar will be replaced by xxx.yyy.zzz.jar, that virtually removes it from the EAR.

Related

War task of Gradle build adding extra jars than those from dependencies in build.gradle

We have recently migrated to Gradle build and I have added a war that task that has web.xml and dependent jars along with jar created in the jar task.
task testWar(type: War)
{
archiveName 'test.war'
webXml = file('WebContent/WEB-INF/web.xml')
into ('WEB-INF/lib')
{
from configurations.compile
from jar
}
}
This creates the war but the size of WEB-INF/lib is double the size of the libs actually given in dependencies. It might be adding the jars that the dependent jars depend on. But Ant build just works fine with just the dependent jars.
Is there any way to create war with just the jars provided in dependencies?
Note: https://docs.gradle.org/current/userguide/war_plugin.html didnt help as I need all the jars in dependencies, just want to avoid extra dependent jars
You can use gradle dependencies to know which libraries are causing the inclusion of these additional dependencies.
Once you have that then simply exclude the dependency you want to remove from your package.
dependencies {
compile (group: 'org.quartz-scheduler', name: 'quartz', version: '2.3.0') {
exclude group: 'org.slf4j', module: 'slf4j-api'
}
}

How to build a jar from a multi-module project when using Gradle?

I'm working on a multi-module library project which I build with Gradle. I have a dependency to another module of this project in my dependencies section:
dependencies {
compile project(':my-other-module')
}
My problem is that I want to build a .jar file which only contains the local modules in the final file, not its transitive dependencies. I tried this:
jar {
from project(':my-other-module').configurations.compile.collect { zipTree it }
}
but this added all the transitive dependencies as well. I want to create a .jar which only contains my own files, so the users of this library can have their own versions of transitive dependencies. How can I do so?
Further clarification:
I have dependencies declared in my project to external jars like apache-commons. I want these not to be in my resulting .jar file but I want the users of my library to be able to just add my library as a dependency and let Maven/Gradle download the transitive dependencies. I don't want these transitive dependencies to be in the .jar file I deploy to Maven Central. compileOnly is not an option since the dependencies I use like apache-commons are not provided by a framework or a container. They need to be present as compile dependencies. I just want to build and deploy a .jar file which has all the files in my project which has multiple modules.
I am not sure it'll help you or not but, you can try this.
In your build.gradle file, customize your jar task as follows:
// This closure will return the full directory path of folder where your classes are built
ext.moduleClassPath = { moduleName ->
def classOutputDirConst = "/classes/java/main"
return "${project(":${moduleName}").buildDir}${classOutputDirConst}"
}
// Now jar task will include only the built file of specified project
jar {
from(moduleClassPath("projectName1"), moduleClassPath("projectName2"))
}
Check the reference for the from(SourcePaths) method here: Reference: https://docs.gradle.org/current/dsl/org.gradle.jvm.tasks.Jar.html#org.gradle.jvm.tasks.Jar:from(java.lang.Object[])
Gradle has a compile-only dependency concept, similar to Maven's provided scope:
Compile-only dependencies are distinctly different than regular compile dependencies. They are not included on the runtime classpath and they are non-transitive, meaning they are not included in dependent projects.
The dependencies you don't want can be declared in the compileOnly configuration, rather than compile, eg:
dependencies {
compileOnly 'javax.servlet:servlet-api:2.5'
}
compileOnly is not even visible to unit tests, by default. We change this in a common gradle snippet which we include in each build:
// compileOnly isn't visible to tests by default, add it
plugins.withType(JavaPlugin).whenPluginAdded {
sourceSets {
test.compileClasspath += configurations.compileOnly
test.runtimeClasspath += configurations.compileOnly
}
}
For the second part, for which I believe you want to create a single "fat" jar,
I would suggest creating your jar using the very good Shadow Plugin, rather than manually extending the jar task. By default, the shadow plugin will not include anything in the compileOnly configuration in the resulting jar.

Gradle include jar produced by another project in war

Currently I have two projects with gradle build.gradle. The first is going to create a fat jar file, which I would like to include in a war file. I thought compiling it would be enough, but it doesn't seem to be ending up in the /lib directory of my war file. Anyone have thoughts I am quite new to gradle.
dependencies {
compile project(':JarProject')
providedCompile 'javax.servlet:javax.servlet-api:3.1.0'
providedCompile 'org.apache.tomcat:tomcat-jsp-api:7.0.55'
}
war {
archiveName 'WarProject.war'
from 'JarProject/build/libs'
webXml = file('src/web.xml')
}
Does the second project war need to be in providedRuntime? Or should I publish the jar from the other project in the local maven repo and include it that way?
The War task essentially behaves like a CopyTask with regards to stuff it packs in the war, so the documentation on working with files is useful. In essence, I think you need something like (untested):
from fileTree('JarProject/build/libs') {
into("lib")
}
That being said, using mavenLocal() and publishing there also works, but it can lead to unexpected results when the war includes some old version from local, picking up the jar explicitly from the file system like above is better.
I think the elegant solution would be to use multi project builds and project level dependencies. You would have the two builds as separate projects of the same Gradle build and add the "jar project" as a regular compile dependency.
How have you declared the dependency? I assume you have a multi-project build with subprojects A and B, both using the War plugin. I made an experiment using Gradle 2.4 and if I declare B/build.gradle like this:
apply plugin: 'war'
dependencies {
compile project(':A')
}
then B.war contains WEB-INF/lib/A.jar. If you correctly follow conventions of Gradle War plugin (place web resources in A/src/main/webapp/ and code-related resources in A/src/main/resources/), then A.jar should contain what you want.
see this

How to remove a class from the classpath after Gradle build

I have a jar that I need to include in my dependencies...
compile files('WebContent/WEB-INF/lib/wls-api.jar')
There's a class inside that jar that is causing trouble that I want to not be on the classpath when I run my app after a gradle build.
How do I get rid of that class after doing a gradle build?
it's a bit trickier than it looks like at first blink; because you will modify the original input!
You should create a configuration for the your modified artifact (this is a proof of concept build.gradle snipplet):
apply plugin: 'java'
task filteredJar(type:Jar){
// you may use a remote artifact by configuring a separate configuration for it and using a jar from:
// configurations.theConfig.resolvedConfiguration.resolvedArtifacts
from zipTree('a.jar')
archiveName 'xx.jar'
// use standard Copy/Sync like filters here, for the example i used
include '**/Tool.class'
}
configurations {
z1
}
artifacts {
z1 filteredJar
}
dependencies {
compile project(path: getPath(), configuration: 'z1')
}
or..alternatively, and might be more preferably:
put this magic into some separate project which will repackage this jar into a usebale one.

Transitive project dependencies when dependency has a zip artifact

Typically, I define project dependencies simply:
dependencies {
compile project(':someProject')
}
This works well for java projects. However, I have a project that produces a zip artifact:
artifacts {
archives file: zipDistribution.archivePath, type: 'zip', builtBy: zipDistribution
}
And when I do the dependency definition above, I don't get the zip artifact when I iterate over the dependencies in the compile configuration. A jar file is there, but no zip. This, however, gets the zip file:
dependencies {
compile project(path: ':someProject', configuration: 'archives')
}
Great! It works! Except "someProject" has its own dependencies... and we're not getting those transitively any more. Adding transitive: true to the project dependency didn't help, either.
How can I get the project's zip artifact AND the transitive dependencies associated with it? I can add both flavors of dependencies, but I feel there's got to be a way to do it in one dependency definition.
The archives configuration contains the artifacts to be uploaded, but it doesn't contain any dependencies. Try this instead:
dependencies {
runtime ...
}
artifacts {
runtime zipDistribution // assuming this is a `Zip` task
}
dependencies {
compile project(':someProject')
}
Instead of the Java plugin's runtime configuration, the former script can also add dependencies and artifacts to a custom configuration, which would then have to be reflected in the project dependency (project(path: ..., configuration: ...)).

Resources