Generate different gradle artifacts off single project or source - java

I have a huge legacy EAR project. Current build process uses Ant and I'm trying to convert to gradle.
The old Ant build uses a single source folder and everything is in there (EJB and WAR code together); then Ant uses different tasks to build EJB-JAR and WAR artifacts filtering by the package of interest (my.web.* for the WAR and my.ejb.* for the EJB).
It goes without saying that EJB and WAR heavily reference each other and I guess that's why they are compiled together even though separate artifacts are generated.
I have tried creating a parent EAR project, then separating my.ejb.* in an EJB subproject as well as my.web.* in a WAR subproject but gradle immediately complained about circular dependency and I haven't found a way around.
If above is not possible, then I am looking for recommendations on how to generate the EJB-JAR and WAR artifacts off the same codebase that would be compiled altogether, then include them in the EAR file as 'deploy' dependencies.
I have googled around but I am not very bright at gradle (obviously). Any help will be greatly appreciated.

I think you are falling into the trap that many of us have when trying to convert an existing ant project to Gradle for the first time - you are thinking of things the same 'ol ant way.
Instead, let Gradle do the work for you and it will be easier and the resulting build.gradle file much smaller and more concise.
Thinking in terms of having a parent ear project and a separate war project is good thinking however. But, you should take advantage of the plugins Gradle has to offer to simplify the task of constructing the archives.
Use the 'ear plugin':
https://docs.gradle.org/current/userguide/war_plugin.html
For example:
apply plugin: 'ear'
apply plugin: 'java'
repositories { mavenCentral() }
dependencies {
// The following dependencies will be the ear modules and
// will be placed in the ear root
deploy project(':war')
// The following dependencies will become ear libs and will
// be placed in a dir configured via the libDirName property
earlib group: 'log4j', name: 'log4j', version: '1.2.15', ext: 'jar'
}
ear {
appDirName 'src/main/app' // use application metadata found in this folder
// put dependent libraries into APP-INF/lib inside the generated EAR
libDirName 'APP-INF/lib'
deploymentDescriptor { // custom entries for application.xml:
// fileName = "application.xml" // same as the default value
// version = "6" // same as the default value
applicationName = "customear"
initializeInOrder = true
displayName = "Custom Ear" // defaults to project.name
// defaults to project.description if not set
description = "My customized EAR for the Gradle documentation"
// libraryDirectory = "APP-INF/lib" // not needed, above libDirName setting does this
// module("my.jar", "java") // won't deploy as my.jar isn't deploy dependency
// webModule("my.war", "/") // won't deploy as my.war isn't deploy dependency
securityRole "admin"
securityRole "superadmin"
withXml { provider -> // add a custom node to the XML
provider.asNode().appendNode("data-source", "my/data/source")
}
}
}
Then for your war, use the 'war plugin'. You can create the war as a separate project:
configurations {
moreLibs
}
repositories {
flatDir { dirs "lib" }
mavenCentral()
}
dependencies {
compile module(":compile:1.0") {
dependency ":compile-transitive-1.0#jar"
dependency ":providedCompile-transitive:1.0#jar"
}
providedCompile "javax.servlet:servlet-api:2.5"
providedCompile module(":providedCompile:1.0") {
dependency ":providedCompile-transitive:1.0#jar"
}
runtime ":runtime:1.0"
providedRuntime ":providedRuntime:1.0#jar"
testCompile "junit:junit:4.12"
moreLibs ":otherLib:1.0"
}
war {
from 'src/rootContent' // adds a file-set to the root of the archive
webInf { from 'src/additionalWebInf' } // adds a file-set to the WEB-INF dir.
classpath fileTree('additionalLibs') // adds a file-set to the WEB-INF/lib dir.
classpath configurations.moreLibs // adds a configuration to the WEB-INF/lib dir.
webXml = file('src/someWeb.xml') // copies a file to WEB-INF/web.xml
}
There is another helpful post on creating them as a single project, but that's definitely a bit more 'dicey':
https://discuss.gradle.org/t/single-project-with-jar-ejb-war-and-ear/5874

Related

Export Gradle subproject in jar without adding it to dependencies

I have a Gradle project with 3 subprojects : core, 1_13, and 1_14. The 1_13 and 1_14 depends on the core project, but the final build has to be done on the core project, and I wanted to include the builds of the 1_13 and 1_14 in the jar.
The 1_13 and 1_14 subprojects have deps that aren't in the core subproject.
Actually, I use the sourceSets to include the source files of the 1_13 and 1_14 projects, but the fact that there are dependencies in the subprojects that doesn't exist in the core subproject, and that I can't use apiElements in the dependencies of the core subproject, because if I do a circular import occurs gets me stuck.
Actually, I use this :
sourceSets {
main {
java {
srcDirs 'src', '../1_13/src', '../1_14/src'
resources {
srcDir 'resources'
}
}
}
}
But because the libs are not present in the core subprojects, the build fails.
I precise that I only need the libs at the compile time, and not at runtime. I also can't add the libs to the core subproject because the submodules uses different versions of the same library. The core module is only here to route on whether one or another package should be used.
After continuing to search for a while, I found the way to do exactly what I want using a Gradle task.
Actually, I created a task to combine all the jar files of the different modules :
task buildPlugin(type: Jar, group: 'build') {
// Change the archive name and include all outputs of build of modules
archiveFileName = "${rootProject.name}.jar"
from getRootProject().allprojects.findAll().sourceSets.main.output
// Delete the old jar of the core module, to keep only the finally build jar file
doLast {
var oldJar = new File(project.buildDir, "libs/" + project.name + "-" + project.version + ".jar")
if (oldJar.exists()) {
oldJar.delete()
}
}
}
The cleanest way would be to extract the common logic that is used by 1_13 and 1_14 to a new project (e.g. common) and add 1_13 and 1_14 as real dependencies to the core project.
You could of course hack something like „use the dependencies of the subprojects as dependencies of the core project“ but hacks like this usually cause more trouble later on.

List dependencies that are going to be part of the (shadow) JAR file

I'm using the latest version (7.1.2) of the ShadowJar (Gradle) plugin to build the application's JAR. I'm trying to exclude some dependencies from the resultant JAR, so I have configured the plugin like:
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
...
tasks.withType(ShadowJar) {
final def attributes = [
// ...all the attributes here
]
dependencies {
// Dependencies that are already provided in the lib/ folder of Apache Flink
exclude(dependency("org.apache.flink:flink-clients_2.12"))
exclude(dependency("org.apache.flink:flink-java"))
exclude(dependency("org.apache.flink:flink-streaming-java_2.12"))
}
manifest.attributes(attributes)
mergeServiceFiles()
minimize()
setZip64(true)
}
It's not clear to me what dependencies are actually begin excluded/included — when unzipping and visually analyzing the generated ZIP file(s). I've been trying to compare the generated (ZIP) -shadow file with the one generated by the distZip task, but the -shadow one doesn't have a single JAR on it...only classes.
So I'm wondering if there is a way to print the effective group of dependencies that are going to be part of the final JAR file.
Based on the ShadowJar's documentation, the default configuration is to merge all dependencies from the project's runtimeClasspath, but ./gradlew dependencies --configuration runtimeClasspath doesn't account for the excluded ones.

Including local jar dependency with corresponding source jar in gradle

How can I include a local jar dependency and its corresponding sources jar (for my IDE, Intellij) in Gradle?
I have tried adding a flatDir to lib (a directory in the same parent directory as all the Gradle stuff) under repositories, where lib contains mylib-1.0.jar and mylib-1.0-sources.jar. I then put implementation name: "mylib-1.0" under dependencies. The jar with compiled classes was included, but not the sources.
I also tried creating a local maven repository. In this case, lib contained
lib/xxx/yyy/mylib/1.0/mylib-1.0.jar
and
lib/xxx/yyy/mylib/1.0/mylib-1.0-sources.jar
where xxx.yyy is the group ID. I added
maven {
url uri("lib")
}
under repositories and
implementation group: "xxx.yyy", name: "mylib", version: "1.0"
under dependencies. Still did not work--neither jars were included this time.
I also tried adding a minimal POM in the same directory as both the jars, but that did not change anything.
Any idea as to where I could be going wrong?
Note: I am no expert at using Gradle or Maven.
Edit: Rationale: in case somebody suggests it, I am aware I can just include as a dependency the jar with the compiled class and "link" the sources jar to it in Intellij, but then every time I refresh gradle I have to re-link them.
I provide below the following approaches.
Prior to gradle 5
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
}
Here libs is the directory which contains the list of jar files and it should be location inside your project base directory.
You can also use in the following manner if you have only one jar file. Here libs refer to the directory which contains only one jar file and libs is available in the project base directory.
dependencies {
compile files('libs/your jar file name.jar')
}
If you want to specify a list of jar files, you can use in the following manner.
dependencies {
compile files(‘libs/a.jar’,
‘libs/b.jar’,
‘libs/c.jar’
)
}
In Gradle 5
You have to use in the follwong manner.
dependencies {
externalLibs files('libs/a.jar', 'libs/b.jar')
}

Dependencies not showing up in jar with Gradle?

I am using this build.grade. When I run gradlew build, it generates a jar file only with the source, not the stone.jar in the libs folder. How should I be doing this?
apply plugin: 'java'
apply plugin: 'eclipse'
// Source sets in the project, specify source directories
sourceSets {
main {
java.srcDir("${projectDir}/src/")
resources.srcDir("${projectDir}/src/")
}
}
// Dependencies for the project are stored in the libs directory
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
}
// Control what goes into the JAR
jar {
manifest {
attributes 'Main-Class': 'com.elsea.sublimelauncher.Driver'
}
// Include all the classes except the tests
include("com/elsea/sublimelauncher/**")
}
By default, jar task in gradle builds an executable jar file from your project source files. It will not contain any transitive libs that are needed for your program. It is good for web servers, because they usually keep all jars in a special lib folder, but is not good for many standalone programs.
What you want to do is to create a fat jar, that will contain all classes and resources in a single jar file.
If you used Maven you could see files like 'my-program-v1.0-jar-with-dependcies.jar'
There is a shadow plugin for gradle that can do the same thing. Wiki on github contains all the information about how to use it in your project.

Gradle: How to exclude JAR from a WAR?

I have a multi-project Gradle build structure, where child project depends on a JAR, which I don't want to be in WAR file. I tried "exclude" but it does not work.
The main project script:
apply plugin: 'war'
war {
dependencies {
runtime (project(':childProject')) {
exclude group: 'javax.servlet.jsp', module: 'jsp-api'
}
}
}
The childProject script:
apply plugin: 'java'
dependencies {
compile 'javax.servlet.jsp:jsp-api'
}
I'm doing it this way.
war {
rootSpec.exclude("**/some-*.jar")
}
From the Gradle documentation
The War plugin adds two dependency configurations: providedCompile and
providedRuntime. Those configurations have the same scope as the
respective compile and runtime configurations, except that they are
not added to the WAR archive.
So, in other words, adding an entry to providedCompile or providedRuntime will cause that dependency to be excluded from the war file.
use providedCompile if you have source that relies on some classes for compiling
use providedRuntime if you use it for testing and not compiling.
http://www.gradle.org/docs/current/userguide/war_plugin.html
Example
providedCompile "javax.servlet:servlet-api:2.5"
I realised that providedCompile sometimes introduced dependencies issues for me, e.g. some classes are not found. I then figured out a more flexible solution.
We can just configure required dependencies as 'compile' and then exclude specific jars from the war file by using the following war configuration:
war {
classpath = classpath.filter { file ->
println file.name
(
!file.name.startsWith('gwt-dev') &&
!file.name.startsWith('gwt-user') &&
!file.name.startsWith('guava-gwt') &&
!file.name.startsWith('gwtbootstrap3') &&
!file.name.startsWith('gwtbootstrap3-extras') &&
!file.name.startsWith('servlet-api')
)
}
}
So far, I found this is the cleanest solution for me. I hope it helps.
I had the same problem but i found a generic solution based on the one of Jake W.
In your child-project, without the war plugin, you add your own providedCompile and providedRuntime like this:
configurations {
providedCompile
providedRuntime.extendsFrom providedCompile
}
plugins.withId('java') {
configurations {
compile.extendsFrom providedCompile
runtime.extendsFrom providedRuntime
}
}
In the project with your war file you copy this anywhere you want:
configurations.runtime.allDependencies.withType(ProjectDependency) { ProjectDependency dep ->
Project proj = dep.dependencyProject
evaluationDependsOn(proj.path)
Configuration cfg = proj.configurations.findByName('providedRuntime')
if (cfg != null){
war {
classpath -= cfg
}
}
}
The default behavior of the War task is to copy the content of src/main/webapp to the root of the archive. Your webapp directory may of course contain a WEB-INF sub-directory, which may contain all the dependencies of the runtime [1] configuration to WEB-INF/lib.
So to avoid load of other jar files or to decrease war file size, you may have to exclude jars during packaging. So, try adding rootSpec.exclude("/*.jar")** to exclude jars in war file like below.
war {
archiveName = "newproject.war"
rootSpec.exclude("**/*.jar")
destinationDir = buildDir
}

Categories