In Chapter 1, Getting Started with Gradle, we already created a Java project called FirstGradleProject
. However, the discussion was only limited to the Eclipse plugin tasks. We did not discuss anything about the Java plugin. The Java plugin is part of the Gradle core API, which enables us to build a Java project with supporting tasks such as compiling the Java code, testing the code, assembling binaries to create libraries, and more. It supports conventions over configuration. This means, if we use this plugin, some default configuration is already available to the developer, such as the location of the source code, the location of the compiled class file, and the jar naming convention. Unless we want to override these configurations, we do not need to write a lot of code to work with the default tasks and properties.
To apply the Java plugin, we add a single statement to the build file:
apply plugin: 'java'
Internally, the apply method of the Java plugin is invoked with the project object as the argument and the build script is enabled in order to use all the tasks and properties provided by the Java plugin. To understand the Java plugin, we will create a new Java application (project name Ch04-Java1
) similar to the Java project FirstGradleProject
, which we developed in Chapter 1, Getting Started with Gradle. We will add two new classes, Customer and Order; we will also add a new JUnit or TestNG library dependency to support unit testing functionality for the project.
With the help of this example, we will explore different Java plugin conventions. To be precise, we will try to understand how different tasks work and which default conventions are supported by the Java plugin. Then, in the next section, we will learn how to customize different properties, so that we can create our own configuration in the build file.
To understand conventions, let us start with the Java plugin tasks. Once we have applied the Java plugin to display all the available tasks in the project (project name Ch04-Java1
), we can use the tasks command:
$ gradle tasks --all ... Build tasks ----------- assemble - Assembles the outputs of this project. [jar] build - Assembles and tests this project. [assemble, check] buildDependents - Assembles and tests this project and all projects that depend on it. [build] buildNeeded - Assembles and tests this project and all projects it depends on. [build] classes - Assembles classes 'main'. compileJava - Compiles Java source 'main:java'. processResources - Processes JVM resources 'main:resources'. clean - Deletes the build directory. jar - Assembles a jar archive containing the main classes. [classes] testClasses - Assembles classes 'test'. [classes] compileTestJava - Compiles Java source 'test:java'. processTestResources - Processes JVM resources 'test:resources'. ... Documentation tasks ------------------- javadoc - Generates Javadoc API documentation for the main source code.[classes] ... Verification tasks ------------------ check - Runs all checks. test - Runs the unit tests. Rules ----- Pattern: clean<TaskName>: Cleans the output files of a task. Pattern: build<ConfigurationName>: Assembles the artifacts of a configuration. Pattern: upload<ConfigurationName>: Assembles and uploads the artifacts belonging to a configuration. To see all tasks and more detail, run with --all. BUILD SUCCESSFUL
The preceding output displays different build tasks, test tasks, documentation tasks, and other available tasks in the Java plugin. The output also shows the task dependencies between different tasks. For example, task classes internally depend on the compileJava
and processResources
tasks, which compile and process the source code and resources from src/main/java
and src/main/resources
, respectively. Similarly, the compileTestJava
task and processTestResources
task compile and process resources from src/test/java
and src/test/resources
, respectively. The output of all these tasks is compiled classes and resources, which will be created under the build
directory by convention and will be added to the classspath
during the execution of the program. Now, let us explore, with an example, what these tasks mean and which conventions are available by default.
To compile classes only under src/main
, we should use the task classes. The compiled classes will be created under build/classes/
directory.
$ gradle classes :compileJava :processResources UP-TO-DATE :classes BUILD SUCCESSFUL
The testClasses
task compiles and processes test classes and resources, and additionally, executes the classes task. In the following output, you can see that the compileJava
, processResources
, and classes
tasks were executed again but the tasks were marked as UP-TO-DATE
. This is because there was no change in the input and output of those tasks, as we have already executed the classes
task in the last command. After successful execution, you will find a test directory created under the build/classes
folder:
$ gradle testClasses :compileJava UP-TO-DATE :processResources UP-TO-DATE :classes UP-TO-DATE :compileTestJava :processTestResources UP-TO-DATE :testClasses BUILD SUCCESSFUL
The other important task is the test
task. This task helps to execute unit test code written under the src/test
directory. After successful execution, you will find the test results created under the build/test-results
directory:
$ gradle test :compileJava UP-TO-DATE :processResources UP-TO-DATE :classes UP-TO-DATE :compileTestJava UP-TO-DATE :processTestResources UP-TO-DATE :testClasses UP-TO-DATE :test BUILD SUCCESSFUL
You have the assemble
task or the jar
task to package classes and resources into a jar file. The jar
task will only create jar files, whereas, the assemble task helps you to produce other artifacts, including jar. For example, when you apply the war plugin, the jar
task is disabled and is replaced with the war task. By default, the JAR file is named <project-name>.jar
and is created under build/libs
. If you have not set the <project-name>
in the build file, you will get the jar name <project-folder-name>.jar
. This is not good practice if the jar file does not contain any version. You can add the version to the jar file by adding the version property to your project in the build file, which will generate <name>-<version>.jar
. In our example, the project name is Ch04-Java1
and the version property is set to 1.0
in the build file. Therefore, the jar file will be named Ch04-Java1-1.0.jar
. Execute the following command and you will find the jar file under build/libs
:
$ gradle assemble :compileJava UP-TO-DATE :processResources UP-TO-DATE :classes UP-TO-DATE :jar :assemble BUILD SUCCESSFUL
Another task is the build
task, which executes the check
and assemble
tasks together. The clean
task deletes all the artifacts created by another task. It actually deletes the complete build/
folder. This means, the clean
task deletes the output generated by all the tasks, that is, check
and assemble
. To delete a task-specific output, we can apply the clean<TaskName>
rule. For example, to delete only the jar file created by the build
task, we can execute the gradle cleanJar
command.
All the tasks in the Java plugin execute based on conventions such as source directory location, build folder name, test result folder, and so on. To understand this in a better way, the following example shows some of the conventions supported by Gradle:
task displayJavaPluginConvention << { println "Lib Directory: $libsDir" println "Lib Directory Name: $libsDirName" println "Reports Directory: $reportsDir" println "Test Result Directory: $testResultsDir" println "Source Code in two sourcesets: $sourceSets" println "Production Code: ${sourceSets.main.java.srcDirs}" println "Test Code: ${sourceSets.test.java.srcDirs}" println "Production code output: ${sourceSets.main.output.classesDir} & ${sourceSets.main.output.resourcesDir}" println "Test code output: ${sourceSets.test.output.classesDir} & ${sourceSets.test.output.resourcesDir}" }
The output displays various conventions supported by the Java plugin. You can find the complete list in the official Gradle documentation at https://docs.gradle.org/current/userguide/java_plugin.html.
$ gradle displayJavaPluginConvention :displayJavaPluginConvention Lib Directory: <path>/build/libs Lib Directory Name: libs Reports Directory: <path>/build/reports Test Result Directory: <path>/build/test-results Source Code in two sourcesets: [source set 'main', source set 'test'] Production Code: [<path>/src/main/java] Test Code: [<path>/src/test/java] Production code output: <path>/build/classes/main & <path>/build/resources/main Test code output: <path>/build/classes/test & <path>/build/resources/test BUILD SUCCESSFUL
Sometimes, these default configurations might not suffice. We might need to configure some default properties to support our requirements. In the next section, we will explore how to configure some of the default configurations.
In the previous example, we learned about the default properties or conventions available in the Java plugin. Now, we will configure some of these properties. This is important when we want to change the build directory name, the libs folder name, or the source file location of the project.
The source- related configuration changes can be set in the sourceSets
closure. The upcoming code snippet (project name Ch04-Java2
) shows that the source code location has been modified from src/main/java
to src/productioncode
for the source code location and src/test/java
to src/testcode
for the test code location, respectively. As a result, compiled classes will now be stored in classes/productioncode
and classes/testcode
locations for the source and test code, respectively. This will not replace the source directory from main
to productioncode
, but Gradle will now look for source code in both main
and productioncode
directories and for test code in both test
and testcode
directories. If you want Gradle to look for the source code only in the productioncode
directory, you can set the java.srcDirs
property.
These Java plugin conventions are written in the JavaPluginConvention
and
BasePluginConvention
classes. One such property, testResultsDirName
, can also be set in the build
file:
buildDir = 'buildfolder' libsDirName = 'libfolder' sourceSets { main { java { srcDir 'src/productioncode/java' } resources { srcDir 'src/productioncode/resources' } } test{ java { srcDir 'src/testcode/java' } resources { srcDir 'src/testcode/resources' } } } testResultsDirName = "$buildDir/new-test-result" sourceSets.main.output.classesDir "${buildDir}/classes/productioncode/java" sourceSets.main.output.resourcesDir "${buildDir}/classes/productioncode/resources" sourceSets.test.output.classesDir "${buildDir}/classes/testcode/java" sourceSets.test.output.resourcesDir "${buildDir}/classes/testcode/resources"
These changes will make sure that buildfolder
, libfolder
, and test-result
folders have been replaced with buildfolder
, libfolder
, and new-test-result
folders.
Figure 4.1 shows the directory structure of the src
folder and new the buildfolder
:
All these new changes can be verified by executing the previously created displayJavaPluginConvention
task. After executing the task, you will find the output updated with new configurations:
$ gradle displayJavaPluginConvention :displayJavaPluginConvention Lib Directory: <path>/buildfolder/libfolder Lib Directory Name: libfolder Reports Directory: <path>/buildfolder/reports Test Result Directory: %path%/buildfolder/new-test-result Source Code in two sourcesets: [source set 'main', source set 'test'] Production Code: [<path>/src/main/java, <path>/src/productioncode/java] Test Code: [<path>/src/test/java, <path>/src/testcode/java] Production code output: <path>/buildfolder/classes/productioncode/java & <path>/buildfolder/classes/productioncode/resources Test code output: <path>/buildfolder/classes/testcode/java & <path>/buildfolder/classes/testcode/resources BUILD SUCCESSFUL
18.219.63.95