Why gradle removes dependency when I build a project - java

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.

Related

Gradle dependencies: package does not exist

I know this has been asked multiple times but the questions have multiple answers.
I'm trying to use a Java package that's a dependency of my dependency. Let's say I've built this gradle project called "ee_tools". And that has a dependency called "my_models", which has a package called "com.mycompany.my_models.db". So in the ee_tools project build.gradle, I've got
dependencies {
// My stuff
implementation group: "com.mycompany", name: "my_models", version: "1.0.0"
}
Then in my current project, I've got
dependencies {
// My stuff
implementation group: "com.mycompany", name: "ee_tools", version: "1.0.0"
Shouldn't this mean that the public classes in my_models are accessible through ee_tools to my current project? Gradle was able to find both in my artifactory instance. And the gradle dependencies command shows ee_tools under the compileClasspath, implementation, and testCompileClasspath trees, but not with its children dependencies. It also shows up in the runtimeClasspath and testRuntimeClasspath trees with its children dependencies, including my_models.
I am also able to see that package inside the jar on the left side of IntelliJ, under the "External Libraries" tree, along with some classes.
But when I try to use the my_models package in my current project, IntelliJ can't find it and it fails a gradle build with the error
error: package com.company.my_models.db does not exist
It can't find any package in that dependency. What gives? Am I declaring the dependencies incorrectly? Or is this a gap between the gradle binary in my command line vs IntellJ and gradlew?
If ee_tools depends on my_models, then your gradle file in ee_tools should be like
implementation project(path: ":path:to:my_models", configuration: 'default')
:path:to:my_models is defined in settings.gradle in project root path like this:
include ':path:to:my_models'

How to generate, compile, jar, and depend on a gradle module

I have a Java Gradle project that uses an OpenAPI specified API. I've used the org.openapi.generator plugin which generates sources as well as a complete Gradle module.
I expect that there's a way to define the generate, compile, jar steps such that I can have other modules depend on the generated module.
I.e.
# api/build.gradle:
plugins {
id 'java'
id "org.openapi.generator" version "5.0.0"
}
repositories {
mavenCentral()
}
dependencies {
testImplementation group: 'junit', name: 'junit', version: '4.12'
}
compileJava.dependsOn "openApiGenerate"
openApiGenerate {
generatorName = "java"
inputSpec = "$projectDir/src/main/openapi/spec.yaml".toString()
outputDir = "$buildDir/generated"
apiPackage = "com.example.api"
invokerPackage = "com.example.api.invoker"
modelPackage = "com.example.api.model"
configOptions = [
dateLibrary: "java8",
library : "native"
]
groupId = "com.example"
id = "api"
}
gradlew api:openApiGenerate generates (extraneous files elided):
api/build/generated/
├── build.gradle
├── pom.xml
├── settings.gradle
└── src
├── main/java/...
└── test/java/...
Is there some way I can delegate-to, include, or depend on this generated module from other modules in the project? The generated module has a reliable group:artifact:version coordinate.
I.e. I'd like to be able to specify com.example:api:1.0 elsewhere in the project.
I've had a read through of https://docs.gradle.org/current/userguide/composite_builds.html as that seemed to be close to what I expect, but I am new to Gradle and it was a little to deep.
I've tried overriding the main and test source sets in api/build.gradle but I dislike having to copy and paste the dependencies from the api/build/generated/build.gradle.
I found https://docs.gradle.org/current/userguide/declaring_dependencies.html#sec:dependency-types which includes a tantalizing example but falls down as it is a source-only dependency.
dependencies {
implementation files("$buildDir/classes") {
builtBy 'compile'
}
}
I looked at this example but how do I depend on a project (api/build/generated/) that does not exist yet?
dependencies {
implementation project(':shared')
}
Great question! I don’t have a perfect answer but hopefully the following will still help.
Suggested Approach
I would keep the builds of the modules that depend on the generated API completely separate from the build that generates the API. The only connection between such builds should be a dependency declaration. That means, you’ll have to manually make sure to build the API generating project first and only build the dependent projects afterwards.
By default, this would mean to also publish the API module before the dependent projects can be built. An alternative to this default would indeed be composite builds – for example, to allow you to test a newly generated API locally first before publishing it. However, before creating/running the composite build, you would have to manually run the API generating build each time that the OpenAPI document changes.
Example
Let’s say you have project A depending on the generated API. Its Gradle build would contain something like this:
dependencies {
implementation 'com.example:api:1.0'
}
Before running A’s build, you’d first have to run
./gradlew openApiGenerate from your api project.
./gradlew publish from the api/build/generated/ directory.
Then A’s build could fetch the published dependency from the publishing repository.
Alternatively, you could drop step 2 locally and run A’s build with an additional Gradle CLI option:
./gradlew --include-build $path_to/api/build/generated/ …
Idea for Less Manual Work
I have thought quite a bit about this but didn’t come up with any complete solution – hence my imperfect suggestion above. Let me still summarize my idea for how this could work.
You would have a Gradle build which generates the API – similar to your api project. That build would also be committed to your VCS.
That build would publish the generated API, even if it wouldn’t produce it itself. Instead, it would somehow delegate to the Gradle build generated by the openApiGenerate task. The delegation would have to happen via a GradleBuild task.
Here lies the crux: all information on dependencies and published artifacts would effectively have to be retrieved via the Gradle CLI. I doubt that that’s currently possible.
Projects that dependend on the API could then include the api-like Gradle project in a composite build without requiring the manual hassle from the approach above.
To expand on #Chriki's answer with what I've actually used:
Define api/ as it's own project with an empty api/settings.gradle file.
This tells gradle that it is a self-contained project.
Define the api module with:
# api/build.gradle
plugins {
id 'java'
id "org.openapi.generator" version "5.0.0"
}
repositories {
mavenCentral()
}
openApiGenerate {
generatorName = "java"
inputSpec = "$projectDir/src/main/openapi/specification.yaml"
outputDir = "$buildDir/generated"
apiPackage = "com.example.api"
invokerPackage = "com.example.api.invoker"
modelPackage = "com.example.api.model"
configOptions = [
dateLibrary: "java8",
library : "native"
]
groupId = "com.example"
id = "api"
version = "1.0.0"
}
Note the group and id (and version) explicitly define its maven coordinate.
Include the build with a substitution so that the dependents can just use its maven coordinate:
# settings.gradle
includeBuild('api/build/generated') {
dependencySubstitution {
substitute module('com.example:api') with project(':')
}
}
... and in some other module:
# app/build.gradle
dependencies {
implementation group: 'com.example', name: 'api'
}
The main advantage of this over ./gradlew --include-build api/build/generated is that [my] IDE will 'link' it all up too.
Generate the API library:
./gradlew --project-dir api/ openApiGenerate
Build/run the main project:
./gradlew build
./gradlew run

how to exclude test dependences in gradle

first, I have a project like this:
project-a
src
main
java
A.java
test
java
ATest.java
then, I have another project like this:
project-b
src
main
java
B.java
test
java
BTest.java
the build.gradle configuration, project-b dependence project-a
dependencies{
compile project(":project-a")
}
the question is BTest.java can access ATest.java, how to avoid this?
-------------------show more detail---------------
settings.gradle
rootProject.name = 'test-dependence'
include 'project-a', 'project-b'
project-b/build.gradle
dependencies {
compile project(":project-a")
testCompile group: 'junit', name: 'junit', version: '4.12'
}
Unfortunately there's a bit of an impedence mismatch between Gradle modules and IntelliJ modules since Gradle allows multiple classpaths (configurations) in a module and IntelliJ has a single classpath per module.
Basically IntelliJ will allow BTest.java to access ATest.java but if you built from command line, Gradle won't allow it.
Try the following in intellij Gradle Settings.
Preferences -> Build, Execution, Deployment -> Build Tools -> Gradle: check create separate modules per source set
Related question here

Gradle add library to classpath

I am using Gradle for a project. My build.gradle has got JUnit as a dependency, but every time I import the project again it does not recognize the JUnit library and asks me to add it to classpath. This problem does not only apply to JUnit, but also to other dependencies. Did I add JUnit to the wrong place?
PS: The project builds just fine, it's just the IDE (IntelliJ) that's marking everything red.
dependencies {
testCompile group: 'junit', name: 'junit', version: '4.11'
}
The dependency declaration is correct. Probably you're opening the project in IntelliJ IDEA the wrong way.
I suggest you to open a Gradle project this way:
Click the File | Open menu
Select the build.gradle file
Here is further information about importing Gradle projects (see also the side note in that page).
download the external jar file from mvn repository and add it to libs folder.
Gradle-project(your project)->libs(inside libs folder paste jar file). if you don't have libs folder just create it.
go to build.gradle
dependencies { compile files('libs/your_jar.jar') }
reload the gradle project.
it's worked for me. i hope it's work for you guys also...

Gradle Multi-Module Project Setup

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.

Categories