I've recently started using Gradle and replacing my existing Maven-based projects. I have had many issues in the past with handling multi-module builds with Maven. Gradle has been a breath of fresh air when handling multi-module buils, but it's not perfect yet.
I have the following folder layout for my projects:
-- Projects
---- EnterpriseApp1
------ EarProject
-------- build.gradle
------ EjbProject
-------- build.gradle
------ WarProject
-------- build.gradle
------ properties.gradle
------ build.gradle
---- CommonLib
------ build.gradle
---- ClientApplication
------ build.gradle
The problem I am having is that the "EnterpriseApp1" and "ClientApplication" both depend on the CommonLib project. I don't know how to configure my "EnterpriseApp1" build file to use the CommonLib project as dependency for the "EjbProject". I have come very close to getting this to work, but not quite working yet. I have had success by copying the CommonLib folder inside "EnterpriseApp1", but that's not a long term solution.
Here's my current properties.gradle file in "EnterpriseApp1":
include "EarProject", "EjbProject", "WarProject"
includeFlat "CommonLib"
According to the Gradle documentation the "includeFlat" command in the "settings.gradle" file will include projects on the same level as the folder where the "settings.gradle" file resides (desired behavior).
EnterpriseApp1/build.gradle file:
subprojects {
apply plugin: 'java'
sourceCompatibility = 1.6
group = 'org.example'
version = '1.0-SNAPSHOT'
repositories {
mavenCentral()
...
}
dependencies {
}
}
EnterpriseApp1/EjbProject/build.gradle:
apply plugin: 'java'
sourceCompatibility = 1.6
repositories {
mavenCentral()
...
}
dependencies {
compile project(':CommonLib')
compile group: 'org.restlet.jee', name: 'org.restlet', version: '2.0.11'
compile group: 'ma.glasnost.orika', name: 'orika-core', version: '1.0'
...
compile group: 'javax.jmdns', name: 'jmdns', version: '3.4.1'
}
When I execute "gradle clean build" from the EnterpriseApp1 folder all the dependencies are downloaded as expected and the projects begin to compile (including the CommonLib project), but the EjbProject project fails during the build due to the fact it's missing the CommonLib jar reference. Gradle is not smart enough (or I'm completely clueless ;)) to configure my EjbProject to use the Jar generated from the CommonLib project build stage.
I apologize for the long and complicated setup. I have been working on trying to figure this out for some time now, but have nearly ran out of ideas. I would really appreciate any help for the community.
Thanks!
The directory layout that you've chosen already hints at a good solution. I suggest to have three separate builds: EnterpriseApp1, CommonLib, and ClientApplication. I'd publish CommonLib to a Maven or Ivy repository so that the other two builds can consume it from there. For local development you can publish CommonLib to the local Maven repo (easiest) or a file-based Ivy repo.
Related
I have no idea why gradle is doing this to me. I have a multiproject that looks like this:
|Parent
|client
|common
|service
Parent is just an empty project and client, common and service are gradle java projects:
I have some classes that are used in both client and service, therefore I wanted to create a common project and build a jar that I would later use in both service and client. I can build the common jar, but whenever i try to do 'add dependency on common' and then try to 'refresh gradle', it removes the dependency and fails to build!
This is what I do:
Then I press this because I want to build it:
And it just removes the dependency!!!
This is build.gradle file from client project:
plugins {
id 'org.springframework.boot' version '2.2.5.RELEASE'
id 'io.spring.dependency-management' version '1.0.9.RELEASE'
id 'java'
}
group = 'sot.rest'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'
repositories {
mavenCentral()
}
dependencies {
compile group: 'org.springframework', name: 'spring-web', version: '5.2.4.RELEASE'
implementation 'org.springframework.boot:spring-boot-starter'
// https://mvnrepository.com/artifact/com.google.code.gson/gson
compile group: 'com.google.code.gson', name: 'gson', version: '2.8.6'
}
Please help, Im desperate!!
Check out my answer on your previous question. It should give you an idea how to structure and declare the multi-module gradle projects.
I think that when you add the dependency on module with IntelliJ, it just adds it to the project structure through project settings in IntelliJ. And later, when you hit refresh, IntelliJ configures the project based on Gradle files.
To make it working, the Parent project should also be a Gradle project. If it isn't just add build.gradle and settings.gradle under the Parent directory.
Then in settings.gradle add the subprojects like:
rootProject.name = 'Parend'
include('common') //this adds the module
include('client')
include('service')
And later, in build.gradle files of client and service modules add the common module as dependency with:
dependencies {
implementation project(':common')
//...
}
If you are going to work more with Gradle, you could take a look at this article about overall insight on Gradle.
Edit:
(I understood that when you use implementation it doesn't throw errors)
To work with multimodule project with Gradle, the root project also needs to be a Gradle project. The root might or might not contain any source code, but it needs to have its own Gradle files.
So if your project structure needs to be like:
Root project 'parent'
+--- Project ':client'
+--- Project ':common'
\--- Project ':service'
Then the parent and submodule projects need to be set as Gradle projects.
To make it the parent project needs to have at least a settings.gradle file and declared includes for submodules, similarly to:
rootProject.name = 'parent'
include 'common'
include 'client'
include 'service'
Each of modules (client,common,service) must have a build.gradle files under its directory. Submodules using common, so the service and client must add the common as dependency in their own build.gradle files, like:
dependencies {
implementation project(':common')
//and rest of required dependencies
//testCompile group: 'junit', name: 'junit', version: '4.12'
}
Then you should be able to import public classes from common in those submodules and rebuild or reimport project without error.
Since the parent project doesn't contain any source code, then it doesn't need its own build script, but then build file of all of the submodules needs to declare the java plugin on top of the build file:
plugins {
id 'java'
}
Since you are working with IntelliJ and your project could have different structure previously, then the project structure in IntelliJ setting could be messed up now.
To fix it you could go to File->Project Structure->Modules and remove/reimport the parent module again.
If you don't have many classes now, I'd recommend you to create a new project. In the "New Project" window pick Gradle and uncheck Java in "Additional Libraries and Frameworks". This will create blank Gradle project.
After the project is generated, do a right mouse click on parent, and select New->Module. In new module window again pick Gradle and leave the Java checked (since the submodules will contain source code).
With that, the IntelliJ will automatically include created module to the settings.gradle file of root/parent project and the build file of that module will contain the basic configuration (e.g. the java plugin).
But you still add the dependency of one module in another in the build.gradle file of that module.
Using IntelliJ 2016.2.5, I seem to be unable to make it resolve Gradle dependencies which are in the same project.
Project structure is as follows:
firstModule
-> build.gradle // 1
-> settings.gradle // 2
secondModule
-> build.gradle // 3
-> settings.gradle // 4
Contents of first build.gradle (1):
group 'de.test'
version '1.0-SNAPSHOT'
apply plugin: 'java'
sourceCompatibility = 1.8
repositories {mavenCentral()}
dependencies {}
And settings.gradle (2):
rootProject.name = 'test'
The contents of the second build.gradle (4) are:
group 'de.test'
version '1.0-SNAPSHOT'
apply plugin: 'java'
sourceCompatibility = 1.8
repositories {mavenCentral()}
dependencies {
compile ('de.test:test:1.0-SNAPSHOT')
}
And second settings.gradle (4):
rootProject.name = 'testdep'
Both modules are imported as Gradle projects and are set to auto-import enabled.
I know from maven projects, that IntelliJ - as well as Eclipse - does resolve those dependencies to the respective modules in the project/workspace. But with Gradle it seems to not recognize the dependencies. After every change in the module test I need to run the explicit gradle tasks clean and build before the module testdep seems to pick up the changes. And this process is not even reliable, if I don't change the version. This is most likely because of the gradle caching, but it is annoying, nevertheless.
Using the command line argument --refresh-dependencies is not a real solution because it makes the build times of our project (the one above is only for demo purposes) unbearable. Also, I would love to not having to use the gradle calls explicitly.
Any ideas/improvements how to handle such a situation?
Does it work with Eclipse, any experiences?
Will this be fixed in IntelliJ 2016.3 (I saw some improvements in the gradle area for that release).
What you are trying to do will be possible using the new Composite Builds functionality in Gradle. Support for IntelliJ IDEA is coming soon.
dependencies {
compile 'org.slf4j:slf4j-api:1.7.13'
compile group: 'org.apache.commons', name: 'commons-math3' , version: '+'
testCompile 'junit:junit:4.12'
}
Even if I add this, when I run gradle build, it works, and codes with commons-math3 can be compiled. But when I run a jar file in build/,
it says Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/commons/math3/complex/Complex
But the official Gradle site says, the resource in 'compile' will also be included in 'runtime' dependency. and I also tried adding the commons-math to runtime. but it does not work.
Maybe this is my misunderstood of the dependency system.
How can I include external library from maven repository into a jar file made by the Gradle.
What you are looking for is either the distribution zips produced by the application plugin or the shadow jar (also called fat jar) produced by the shadowJar plugin:
The distribution zip (application plugin)
About the distribution zip
The distribution zips look like this:
my-app-0.2.0.zip
├──bin
│ ├──my-app
│ └──my-app.bat
└──lib
├──my-app-0.2.0.jar
├──slf4j-api.1.7.13.jar
└──commons-math3-3.6.jar
You can then run your application with its dependencies by unzipping what has been produced in build/distributions/ and running either my-app.bat (on windows) or ./my-app (on linux or OS X)
Building a distribution zip
Here is a sample gradle build file for making a distribution zip:
build.gradle
apply plugin: 'java'
apply plugin: 'application'
mainClassName = 'org.myapp.Main'
repositories { jcenter() }
dependencies {
compile 'org.slf4j:slf4j-api:1.7.13'
compile 'org.apache.commons:commons-math3:3.6'
testCompile 'junit:junit:4.12'
}
Can be run with gradle distributionZip or gradle distributionTar. To just run the application, use gradle run.
The shadow jar
About the shadow jar
The shadow jar is one giant jar file that is a combination of your program and its libraries, packed together into one file. You will get a file that is self-contained and can be run by a double-click on most systems (e.g. on Windows that works, on Xubuntu it can be run by right-clicking and selecting 'Run with Oracle Java 8 Runtime', etc...).
Building a distribution zip
Here is, again, a sample build.gradle file:
apply plugin: 'java'
apply plugin: 'com.github.johnrengelman.shadow'
mainClassName = 'org.myapp.Main'
jar {
manifest {
attributes('Main-Class': mainClassName)
}
}
buildscript {
repositories { jcenter() }
dependencies {
classpath 'com.github.jengelman.gradle.plugins:shadow:1.2.2'
}
}
repositories { jcenter() }
dependencies {
compile 'org.slf4j:slf4j-api:1.7.13'
compile 'org.apache.commons:commons-math3:3.6'
testCompile 'junit:junit:4.12'
}
Run it with gradle shadowJar - Your jar with packed dependencies will be in build/libs and it will be named my-app-x.x.x-all.jar.
Gradle is first of all a build tool (just like maven, btw).
Its "responisiblity" starts when you feed it a source file and ends when you get your artifact (in your case its a jar).
Now when you're going to actually run your application there is a plethora of different options here.
If you just run java -jar <your_jar> you are responsible by yourself to construct the classpath.
If you run it with some kind of external runner, you should read the documentation of it and supply it a classpath.
Hope this helps
Following the explanation in "Building and Testing with Gradle" I have a multiproject gradle setup like this:
rootFolder
build.gradle
settings.gradle
EMS
build.gradle
cloud-sdk
build.gradle
The cloud-sdk project depends on several jars, partially resolved via maven partially via locale jars:
// file: cloud-sdk/build.gradle
apply plugin: 'java'
repositories {
mavenCentral()
}
dependencies {
compile group:'org.apache.tomcat', name:'tomcat-catalina', version:'7.0.47'
compile group:'org.mongodb', name:'mongo-java-driver', version:'2.11.3'
compile group:'com.google.code.gson', name:'gson', version:'2.2.4'
compile group:'com.thoughtworks.xstream', name:'xstream', version:'1.4.6'
compile fileTree(dir:'lib/', include:'JavaPNS_2.2.jar')
compile fileTree(dir:'lib/', include:'gcm-server.jar')
}
The EMS-project depends on the cloud-sdk which I think should be defined like this:
// file: EMS/build.gradle
apply plugin: 'java'
dependencies {
compile project(':cloud-sdk')
}
Furthermore, my root build.gradle and settings.gradle files look like this:
settings.gradle
include 'cloud-sdk', 'EMS'
build.gradle
apply plugin: 'java'
dependencies {
compile project(':EMS')
}
In this case I am not sure whether I also need the dependency compile project (':cloud-sdk'). I tried both version but since I get the same error message in both cases I assume it doesn't matter.
When I try to run the script from the rootFolder via gradle build I get the following error messages:
Could not resolve all dependencies for configuration ':EMS:compile'.
> Could not find org.apache.tomcat:tomcat-catalina:7.0.47.
Required by:
rootFolder:EMS:unspecified > rootFolder:cloud-sdk:unspecified
> Could not find org.mongodb:mongo-java-driver:2.11.3.
Required by:
rootFolder:EMS > rootFolder:cloud-sdk:unspecified
> Could not find com.google.code.gson:gson:2.2.4.
Required by:
rootFolder:EMS > rootFolder:cloud-sdk:unspecified
> Could not find com.thoughtworks.xstream:xstream:1.4.6.
Required by:
rootFolder:EMS:unspecified > rootFolder:cloud-sdk:unspecified
But when I just build the cloud-sdk project via gradle cloud-sdk:build gradle downloads the required jars and builds the project without a problem.
But even if I try gradle build after that, although gradle notices that the cloud-sdk project is already up-to-date, it complains about the missing dependencies.
Why is that? It downloaded them already so they should be available somewhere and even if not the cloud-sdk knows what it needs and how to fetch it. What am I missing? Do I need to specify the dependencies in some other way?
Ok, it turns out gradle could not fetch the dependencies in the EMS project since I did not specify any repositories to fetch them from. I assumed that would not be necessary since the only dependencies I needed it to fetch were declared in the cloud-sdk project and that did have a repository given.
This is basically the solution to my problem, but if anybody can explain to me why it is necessary to specify the repository again or explain why it is a bug in gradle that should be fixed, I'll accept that answer as it would answer the "why" and not just the "how do I get it to work".
I'm developing a project with gradle. My build file is almost empty so far:
apply plugin: 'java'
apply plugin: 'eclipse'
version = '0.1'
repositories {
mavenCentral()
}
dependencies {
testCompile group: 'junit', name: 'junit', version: '4.+'
}
My project depends on a Maven project. Precisely this project: http://git.eclipse.org/c/bpmn2/tree/org.eclipse.bpmn2
I've cloned this project into my workspace, but I don't know the best way to declare the dependency in my build.gradle file. This is what I've done so far:
dependencies {
compile files ("C:/path/to/org.eclipse.bpmn2-0.7.0-SNAPSHOT.jar")
}
But this way I have to manually build the maven project. Does somebody know a better way of doing this dependency management?
I'm using Eclipse Gradle Integration and I've noticed an interesting eclipse project property:
Gradle - Dependency Management
[x] Remap Jars to maven projects (requires Gradle 1.1 and m2e)
This seems to do what I need. But I don't know how to use this feature...
Thanks in advance.
If the Maven project is not available in any Maven repo, Gradle can't find it anywhere, so you'll have to build it. I would at least mvn install it, and tell Gradle to look for artifacts in your local Maven repo rather than in a specific directory, using
repositories {
mavenLocal()
}
The eclipse-integration-gradle plugin replaces the mavenLocal() jar dependency with a Eclipse project dependency. This is the easiest way I've found so far. See: http://forum.springsource.org/showthread.php?139634-How-to-use-quot-remap-Jars-to-maven-projects-quot-feature