Gradle's Dependency Management
Gradle's dependency management allows quick resolution of library dependency.
In this tutorial you will learn how to resolve library dependency using Gradle's dependency management. In order to appreciate this feature of Gradle, you will first resolve the dependency problem using the manual approach.
Prerequisite:
You are required to do the Gradle Basics Tutorial.
You are not required (but recommended) to do the Git Basics Tutorial.
- However, ensure that you have Git client installed in your machine.
Copy Sample Codes from Git repository
Open a terminal window and create the directory
gradletemp
in the root directory. Go to the created directory.> mkdir gradletemp > cd gradletemp
Clone the git repository
https://github.com/pong-pantola/gradle-dependency-management.git
and go to the createdgradle-dependency-management
directory.> git clone https://github.com/pong-pantola/gradle-dependency-management.git > cd gradle-dependency-management
The
gradle-dependency-management
directory has two subdirectories:src
andbuild
.gradle-dependency-management/ | |----src/main/ | | | |----java/net/tutorial/ | | | | | |----Math.java | | |----Calculator.java | | | |----resources/ | | | |----log4j.properties | |----build/ | |----classes/ |----libs/
src
has a subdirectorymain
.src/main
contains the Java classsrc/main/java/net/tutorial/Math.java
which contains methods performing mathematical functions (i.e., add, sub, mulitiply). In addition, it contains thesrc/main/java/net/tutorial/Calculator.java
which is a sample Java application that usesMath.java
.src/main
also has a subdirectoryresources
. Any resource (e.g., configuration or properties file) needed by the Java classes can be placed here. For this tutorial, the subdirectory contains the log4j.properties file. It is not essential in this tutorial to know the purpose of this file aside from it is used by one of the Java classes.build
has two subdirectories:classes
andlibs
.build/classes
is used to hold the the.class
files that will be created later when you compile your.java
files.build/libs
is used for the libraries (i.e,.jar
files) that you will download later. These libraries are needed to compile the Java classes later.
Examine the Java classes
Math.java
contains the methodsadd
,sub
, andmultiply
. This is exactly the same file that was discussed in JUnit Basics Tutorial.Since the focus of this tutorial is on Gradle's dependency management, you don't need to analyze at this point the contents of
Math.java
. This class was discussed in the JUnit Basics Tutorial and will be revisited in Gradle's Unit Testing Tutorial.Note that the method
add
has a logical error (i.e., instead ofa+b
;, the return statement isa-b;
). This error will be discussed further in the Gradle's Unit Testing Tutorial. No need to fix this error at this point.In addition, a delay is inserted in the method
multiply
. This will also be discussed in the Gradle's Unit Testing Tutorial.Calculator.java
is a Java application that usesMath.java
.Source code of
src/main/java/net/tutorial/Calculator.java
:package net.tutorial; import org.apache.log4j.Logger; public class Calculator{ private static final Logger LOGGER = Logger.getLogger(Calculator.class); public static void main (String args[]){ Math m = new Math(); System.out.println("5 + 9 = " + m.add(5, 9)); System.out.println("8 - 2 = " + m.sub(8, 2)); System.out.println("4 x 7 = " + m.multiply(4, 7)); LOGGER.info("Calculation completed."); } }
Calculator.java
is very similar to the one discussed in JUnit Basics Tutorial. However, in this tutorials these three extra lines are included:import org.apache.log4j.Logger;
private static final Logger LOGGER = Logger.getLogger(Calculator.class);
LOGGER.info("Calculation completed.");
Log4j is a Java-based logging library. For this tutorial, you don't need to know the details regarding this library. The three Log4j-related lines above are included in
Calculator.java
just to demonstrate dependency resolution. WhenCalculator.java
is compiled later, you will encounter an error due to Log4j library dependency. You will resolve this dependency using the manual approach as well as Gradle's dependency management.It was mentioned earlier that the
src/main
directory has a subdirectoryresources
that contains the filelog4j.properties
. This file is needed by Log4j. However, in this tutorial it is not important to examine the contents of the file.Try to compile
Math.java
andCalculator.java
.Make sure that you are in the
gradle-dependency-management
directory before issuing the command below.> javac -d build/classes/main src/main/java/net/tutorial/*.java
Output:
src\main\java\net\tutorial\Calculator.java:3: error: package org.apache.log4j does not exist import org.apache.log4j.Logger; ^ src\main\java\net\tutorial\Calculator.java:7: error: cannot find symbol private static final Logger LOGGER = Logger.getLogger(Calculator.class); ^ symbol: class Logger location: class Calculator src\main\java\net\tutorial\Calculator.java:7: error: cannot find symbol private static final Logger LOGGER = Logger.getLogger(Calculator.class); ^ symbol: variable Logger location: class Calculator
As expected, a compile-time error is encountered due to the dependency to the Log4j library. You will solve this problem by resolving the Log4j library dependency.
Manually Resolve the Library Dependency Problem
In order to appreciate Gradle, let's try resolving library dependency without using Gradle. After this, you will use Gradle's dependency management to see how dependency resolution becomes simple with the use of Gradle.
Go to http://logging.apache.org/log4j/2.x/download.html.
Just in case the URL is broken, you may go to Apache Log4j and find the download link.
Download the latest version of Apache Log4j library (i.e., apache-log4j-x.x-bin.zip OR apache-log4j-x.x-bin.tar.gz) in a temporary directory.
Extract the contents of the zip (or gz) file. You will see several Log4j
.jar
files. Copy the following.jar
files in the subdirectorybuild/libs
:log4j-y.y-api-x.x.jar
(e.g.,log4j-1.2-api-2.5.jar
)log4j-api-x.x.jar
(e.g.,log4j-api-2.5.jar
)log4j-core-x.x.jar
(e.g.,log4j-core-2.5.jar
)
Note that for the compilation to work, you need to select the necessary
.jar
files. In a non-tutorial environment, you will be the one who will identify which.jar
files are necessary which makes manual dependency resolution much harder.Compile again
Math.java
andCalculator.java
.Note that the command below includes the
-classpath build/libs/*
option. This is needed so that the compilation will use the.jar
files that you copied earlier.> javac -classpath build/libs/* -d build/classes/main src/main/java/net/tutorial/*.java
The compilation is successful. The library dependency is manually resolved.
Take note that in manual library dependency resolution you need to know:
a. where to download the library
b. which
.jar
files are neededRun the
Calculator
application.Note that the command below includes the
-Dlog4j.configurationFile=file:src//main/resources/log4j.properties
option. This is needed so that Log4j knows the location of its.properties
file.> java -classpath build/libs/*;build/classes/main -Dlog4j.configurationFile=file:src//main/resources/log4j.properties net/tutorial/Calculator
Output:
5 + 9 = -4 8 - 2 = 6 4 x 7 = 28 INFO - Calculator - Calculation completed.
As mentioned earlier the method
sum
has a logical error (i.e., sum should be 14 and not -4). In addition, the methodmultiply
has a delay that is why you have observed a 3 secs. delay before4 x 7 = 28
appeared. The logical error and the delay are not important in this tutorial. These will be useful in Gradle's Unit Testing Tutorial.At this point, what is important to note is the compilation (and execution) became successful due to the manual dependency resolution.
Resolve the Library Dependency Problem using Gradle's Dependency Management
One of the features of Gradle is dependency management. By just specifying the libraries needed in a Gradle build script file (
build.gradle
), dependency resolution becomes faster.
To undo the files created during the manual dependency resolution (e.g., the
.jar
files you copied earlier), delete the entirebuild
subdirectory.In Gradle, the
build
subdirectory and its subdirectories (e.g.,classes
) need not exist for compilation to work.In the
gradle-dependency-management
directory, create a text file with a filenamebuild.gradle
.Place the following line in
build.gradle
:apply plugin: 'java'
The line
apply plugin: 'java'
adds Java compilation along with testing and bundling capabilities in the Gradle project.At this point, Gradle's dependency management is not yet utilized in
build.gradle
. Let's try to compile the.java
files using Gradle and see what errors will be produced.To compile the
.java
files, use Gradle'sassemble
task:Make sure that you are in the
gradle-dependency-management
directory before issuing the command below.> gradle assemble
Output:
:compileJava /gradle-dependency-management/src/main/java/net/tutorial/Calculator.java:3: error: package org.apache.log4j does not exist import org.apache.log4j.Logger; ^ /gradle-dependency-management/src/main/java/net/tutorial/Calculator.java:7: error: cannot find symbol private static final Logger LOGGER = Logger.getLogger(Calculator.class); ^ symbol: class Logger location: class Calculator /gradle-dependency-management/src/main/java/net/tutorial/Calculator.java:7: error: cannot find symbol private static final Logger LOGGER = Logger.getLogger(Calculator.class); ^ symbol: variable Logger location: class Calculator 3 errors :compileJava FAILED FAILURE: Build failed with an exception. * What went wrong: Execution failed for task ':compileJava'. > Compilation failed; see the compiler error output for details. * Try: Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. BUILD FAILED Total time: 4.751 secs
As expected compilation error due to Log4j dependency is encountered. Let's fix this problem by utlizing the dependency management of Gradle in
build.gradle
.Specify the library repository to be used in
build.gradle
by updating the file to the following:apply plugin: 'java' repositories { mavenCentral() }
Repositories are dependency containers. It has a collection of libraries that your code may need. You may specify zero or more repositories in a
build.gradle
file.In this tutorial, you need one repository to resolve the dependency problem caused by the use of the Log4j library.
Repositories can be local (e.g., a local directory in your hard drive) or remote (e.g., Maven or Ivy repository). This tutorial does not cover the details on how to use local repositories as well as the difference between Maven and Ivy repositories. You may check Gradle's website for additional information on repositories.
In
build.gradle
, you added the following lines earlier:repositories { mavenCentral() }
This means that you will be using Maven's central repository to get the necessary library.
Specify the needed library in
build.gradle
by updating the file to the following:apply plugin: 'java' repositories { mavenCentral() } dependencies { compile 'log4j:log4j:1.2.17' }
The entry
compile 'log4j:log4j:1.2.17'
means that the indicated library ('log4j:log4j:1.2.17'
) is needed during compilation.'log4j:log4j:1.2.17'
uses the following format:'group:name:version'
.To know the group, name, and version of the library you may go to Maven Central Repository and search for the library you need.
As an example, you may go to the Maven Central Repository. In the search box, type
log4j
. The search result will be a table with its first 3 columns labeled as:GroupId
,ArtifiactId
, andLatest Version
, which is basically thegroup
,name
, andversion
mentioned in the format above.You may choose the most appropriate row in the search result. For this tutorial, the row that was selected is the one with the following values:
GroupId ArtifactId Latest Vesion log4j log4j 1.2.17 This is the reason why in
build.gradle
the dependency is specified ascompile 'log4j:log4j:1.2.17'
.Note that when you try this tutorial the available library in Maven may have changed. Adjust the entry in
build.gradle
if necessary (e.g., version higher than1.2.17
may be available already).Compile again the
.java
files using Gradle'sassemble
task.> gradle assemble
Output:
:compileJava :processResources :classes :jar :assemble BUILD SUCCESSFUL Total time: 8.029 secs
Since dependency management of Gradle is already specified in
build.gradle
, the compilation error encountered earlier is resolved.Notice that the subdirectory
build
is automatically created. Below are some of the subdirectories and files that are insidebuild
.gradle-dependency-management/ | |----build/ | |----classes/main | | | |----java/net/tutorial/ | | | |----Math.class | |----Calculator.class | |----libs/ | | | |----gradle-dependency-management.jar | |----resources/main/ | |----log4j.properties
Notice that the
Math.class
andCalculator.class
were created after theassemble
task.More importantly, the
assemble
task generated thegradle-dependency-management.jar
under thelibs
subdirectory. This.jar
file will be executed later. In addition, the log4j.properties file is copied inside a subdirectory ofbuild
. This is needed since the application uses the Log4j library.Run the
.jar
file.> java -jar build/libs/gradle-dependency-management.jar
Output:
no main manifest attribute, in build/libs/gradle-dependency-management.jar
The
no main manifest attribute
error is encountered because you did not specify the entry point of your Java application. The entry point is your.class
file that contains themain
method. In this tutorial, the entry point is theCalculator
class.Specify the entry point of your application in
build.gradle
by updating the file to the following:apply plugin: 'java' repositories { mavenCentral() } dependencies { compile 'log4j:log4j:1.2.17' } jar { manifest { attributes 'Main-Class': 'net.tutorial.Calculator' } }
Reassemble and try to run again the
.jar
file.> gradle assemble > java -jar build/libs/gradle-dependency-management.jar
Output:
Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/log4j/Logger at net.tutorial.Calculator.<clinit>(Calculator.java:7) Caused by: java.lang.ClassNotFoundException: org.apache.log4j.Logger at java.net.URLClassLoader$1.run(Unknown Source) at java.net.URLClassLoader$1.run(Unknown Source) at java.security.AccessController.doPrivileged(Native Method) at java.net.URLClassLoader.findClass(Unknown Source) at java.lang.ClassLoader.loadClass(Unknown Source) at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source) at java.lang.ClassLoader.loadClass(Unknown Source) ... 1 more
The error states that
org.apache.log4j.Logger
is not found. This is due to the Log4j library is not included ingradle-dependency-management.jar
.Inspect the contents of the
.jar
file to verify that the Log4j library is not included:> jar tf build/libs/gradle-dependency-management.jar
If you have an archiving software like
7Zip
, you may use this instead to inspect the contents of the.jar
file.gradle-dependency-management.jar
contains the following:gradle-dependency-management.jar | |----META-INF/ | | | |----MANIFEST.MF | |----net/tutorial/ | | | |----Calculator.class | |----Math.class | |----log4j.properties
There are no classes related to Log4j that are included in the
.jar
file.Specify that Log4j library should be included in the
.jar
file by updating thebuild.gradle
file to the following:apply plugin: 'java' repositories { mavenCentral() } dependencies { compile 'log4j:log4j:1.2.17' } jar { from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } } manifest { attributes 'Main-Class': 'net.tutorial.Calculator' } }
Reassemble and try to run again the
.jar
file.> gradle assemble > java -jar build/libs/gradle-dependency-management.jar
Output:
5 + 9 = -4 8 - 2 = 6 4 x 7 = 28 INFO - Calculator - Calculation completed.
As expected, the Java application executed successfully.
The Log4j library is included in
gradle-dependency-management.jar
.Verify that the Log4j library is already included in the
.jar
file:> jar tf build/libs/gradle-dependency-management.jar
gradle-dependency-management.jar
now contains the following:gradle-dependency-management.jar | |----META-INF/ | | | |----MANIFEST.MF | |----net/tutorial/ | | | |----Calculator.class | |----Math.class | |----org/apache/log4j/... | |----log4j.properties
The subfolder
org/apache/log4j/
and additional subfolders and files are included ingradle-dependency-management.jar
.