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
gradletempin the root directory. Go to the created directory.> mkdir gradletemp > cd gradletempClone the git repository
https://github.com/pong-pantola/gradle-dependency-management.gitand go to the createdgradle-dependency-managementdirectory.> git clone https://github.com/pong-pantola/gradle-dependency-management.git > cd gradle-dependency-managementThe
gradle-dependency-managementdirectory has two subdirectories:srcandbuild.gradle-dependency-management/ | |----src/main/ | | | |----java/net/tutorial/ | | | | | |----Math.java | | |----Calculator.java | | | |----resources/ | | | |----log4j.properties | |----build/ | |----classes/ |----libs/srchas a subdirectorymain.src/maincontains the Java classsrc/main/java/net/tutorial/Math.javawhich contains methods performing mathematical functions (i.e., add, sub, mulitiply). In addition, it contains thesrc/main/java/net/tutorial/Calculator.javawhich is a sample Java application that usesMath.java.src/mainalso 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.buildhas two subdirectories:classesandlibs.build/classesis used to hold the the.classfiles that will be created later when you compile your.javafiles.build/libsis used for the libraries (i.e,.jarfiles) that you will download later. These libraries are needed to compile the Java classes later.
Examine the Java classes
Math.javacontains 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
addhas 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.javais 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.javais 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.javajust to demonstrate dependency resolution. WhenCalculator.javais 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/maindirectory has a subdirectoryresourcesthat 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.javaandCalculator.java.Make sure that you are in the
gradle-dependency-managementdirectory before issuing the command below.> javac -d build/classes/main src/main/java/net/tutorial/*.javaOutput:
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 CalculatorAs 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
.jarfiles. Copy the following.jarfiles 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
.jarfiles. In a non-tutorial environment, you will be the one who will identify which.jarfiles are necessary which makes manual dependency resolution much harder.Compile again
Math.javaandCalculator.java.Note that the command below includes the
-classpath build/libs/*option. This is needed so that the compilation will use the.jarfiles that you copied earlier.> javac -classpath build/libs/* -d build/classes/main src/main/java/net/tutorial/*.javaThe 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
.jarfiles are neededRun the
Calculatorapplication.Note that the command below includes the
-Dlog4j.configurationFile=file:src//main/resources/log4j.propertiesoption. This is needed so that Log4j knows the location of its.propertiesfile.> java -classpath build/libs/*;build/classes/main -Dlog4j.configurationFile=file:src//main/resources/log4j.properties net/tutorial/CalculatorOutput:
5 + 9 = -4 8 - 2 = 6 4 x 7 = 28 INFO - Calculator - Calculation completed.As mentioned earlier the method
sumhas a logical error (i.e., sum should be 14 and not -4). In addition, the methodmultiplyhas a delay that is why you have observed a 3 secs. delay before4 x 7 = 28appeared. 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
.jarfiles you copied earlier), delete the entirebuildsubdirectory.In Gradle, the
buildsubdirectory and its subdirectories (e.g.,classes) need not exist for compilation to work.In the
gradle-dependency-managementdirectory, 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.javafiles using Gradle and see what errors will be produced.To compile the
.javafiles, use Gradle'sassembletask:Make sure that you are in the
gradle-dependency-managementdirectory before issuing the command below.> gradle assembleOutput:
: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 secsAs 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.gradleby 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.gradlefile.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.gradleby 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, andversionmentioned 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.gradlethe 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.gradleif necessary (e.g., version higher than1.2.17may be available already).Compile again the
.javafiles using Gradle'sassembletask.> gradle assembleOutput:
:compileJava :processResources :classes :jar :assemble BUILD SUCCESSFUL Total time: 8.029 secsSince dependency management of Gradle is already specified in
build.gradle, the compilation error encountered earlier is resolved.Notice that the subdirectory
buildis 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.propertiesNotice that the
Math.classandCalculator.classwere created after theassembletask.More importantly, the
assembletask generated thegradle-dependency-management.jarunder thelibssubdirectory. This.jarfile 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
.jarfile.> java -jar build/libs/gradle-dependency-management.jarOutput:
no main manifest attribute, in build/libs/gradle-dependency-management.jarThe
no main manifest attributeerror is encountered because you did not specify the entry point of your Java application. The entry point is your.classfile that contains themainmethod. In this tutorial, the entry point is theCalculatorclass.Specify the entry point of your application in
build.gradleby 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
.jarfile.> gradle assemble > java -jar build/libs/gradle-dependency-management.jarOutput:
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 moreThe error states that
org.apache.log4j.Loggeris not found. This is due to the Log4j library is not included ingradle-dependency-management.jar.Inspect the contents of the
.jarfile to verify that the Log4j library is not included:> jar tf build/libs/gradle-dependency-management.jarIf you have an archiving software like
7Zip, you may use this instead to inspect the contents of the.jarfile.gradle-dependency-management.jarcontains the following:gradle-dependency-management.jar | |----META-INF/ | | | |----MANIFEST.MF | |----net/tutorial/ | | | |----Calculator.class | |----Math.class | |----log4j.propertiesThere are no classes related to Log4j that are included in the
.jarfile.Specify that Log4j library should be included in the
.jarfile by updating thebuild.gradlefile 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
.jarfile.> gradle assemble > java -jar build/libs/gradle-dependency-management.jarOutput:
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
.jarfile:> jar tf build/libs/gradle-dependency-management.jargradle-dependency-management.jarnow contains the following:gradle-dependency-management.jar | |----META-INF/ | | | |----MANIFEST.MF | |----net/tutorial/ | | | |----Calculator.class | |----Math.class | |----org/apache/log4j/... | |----log4j.propertiesThe subfolder
org/apache/log4j/and additional subfolders and files are included ingradle-dependency-management.jar.
