Building Java Projects with Gradle
Tags: java, softwaredevelopment, gradle, groovy
Published: 2020-06-10 02:14:27

---
title: Building Java Projects with Gradle
published: true
date: 2020-06-09 10:14:00 EDT
tags: Java,SoftwareDevelopment,Gradle,Groovy
canonical_url: https://hackersandslackers.com/building-java-projects-with-gradle/
---

 ![Building Java Projects with Gradle](https://hackersandslackers-cdn.storage.googleapis.com/2020/02/java-gradle-1.jpg)

I've had a few strongly worded opinions about Java as a language in the past. Be that as it may, choosing a programming language is a luxury that many people don’t have; as long as enterprises exist, there will always be a need for Java developers. According to the [2019 StackOverflow developer survey](https://insights.stackoverflow.com/survey/2019), about 40% of developers are actively using Java in some way.

For those who work mostly in more "modern" programming languages, coming to Java in 2019 has numerous pain points. Installing dependencies by manually downloading and dropping jar files into your Java path is a humbling look into the past where package managers were non-existent. Luckily for us, there are tools like Gradle to help us bridge the gap between older programming languages and the processes we're used to.

## Java Build Tools

Build tools are useful to Java developers in that they automate many processes associated with packaging a build which would otherwise be manual. Build tools not only compile a project's source code, but also download dependencies and run tests. Java build tools aren't exactly a sexy part of anybody's stack - it can be argued that they simply accomplish the things that we would expect a modern programming language to have natively.

Three build tools have historically dominated the scene: [Apache Ant](https://ant.apache.org/) (released 2000), [Apache Maven](https://maven.apache.org/) (released around 2004), and [Gradle](https://gradle.org/) (released 2007). We won't waste time comparing these tools since Gradle is objectively better than the other options. If you're curious as to why, I'll humor you:

- **Ease-of-use** : Gradle build scripts are written in Groovy (Ant and Maven are configured via XML files... enough said?)
- **Performance** : Gradle creates builds faster by only building necessary changes from build-to-build, reusing build outputs from previous builds, and leveraging a running daemon process.
- **Debugging** : Part of Gradle's build process is outputting _very_ useful HTML logs detailing anything that went wrong.
- **Extensible** : Gradle has a vast ecosystem of available [plugins](https://plugins.gradle.org/) which open plenty of opportunities including support for Java, C++, or Python.

## Installing Gradle

If you haven't done so already, go ahead and install Gradle using homebrew

```shell
$ brew install gradle
```

Installing Gradle via homebrew will automatically configure your path and all that nonsense. Verify that Gradle was installed correctly and you should be good to go:

```shell
$ gradle -v

------------------------------------------------------------
Gradle 5.5.1
------------------------------------------------------------
```

## Initiating a Java Project with Gradle

Starting a Java project under normal circumstances is notably obnoxious when compared to other programming languages. Java's philosophy around namespaces results in ridiculously complex folder structures for any Java project, even a simple _hello world_ app. Perhaps the most undersold feature of Gradle is the ability to generate cookie-cutter projects to get started.

Create a new directory using `mkdir myProject` (or whatever you want your project to be).  `cd` into that empty directory and run Gradle's init script:

```shell
$ gradle init
```

Gradle is going to prompt us with a few questions to get more context about what we're creating. The first question Gradle asks us to select a _type_ of project:

```shell
Select type of project to generate:
  1: basic
  2: application
  3: library
  4: Gradle plugin
Enter selection (default: basic) [1..4] 2
```

Creating a **basic** project only initiates a bare minimum project, which isn't particularly useful. Creating an **application** , on the other hand, will initiate a barebones folder structure for the language of your choice.

Select **application (2)** for your type of project. This should prompt three more multiple-choice questions:

```shell
Select implementation language:
  1: C++
  2: Groovy
  3: Java
  4: Kotlin
Enter selection (default: Java) [1..4] 3

Select build script DSL:
  1: Groovy
  2: Kotlin
Enter selection (default: Groovy) [1..2] 1

Select test framework:
  1: JUnit 4
  2: TestNG
  3: Spock
  4: JUnit Jupiter
Enter selection (default: JUnit 4) [1..4] 1

Project name (default: java-gradle-tutorial): 

Source package (default: java-gradle-tutorial): 
```

Since we selected **application** as our project type, Gradle lets us chose from one of four programming languages to build our project (selecting **basic** wouldn't have prompted us with this).

Next, we're able to select either Groovy or Kontlin as the language in which our _build scripts_ will be written in. Most people choose Groovy as it's an easy language to pick up, especially in this context.

Lastly, we're asked to select a test framework to run our unit tests in. JUnit 4 is the most popular, and it's good enough for me.

Check out our folder structure now:

```shell
/java-gradle-tutorial
├── build.gradle
├── gradle
│ └── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── settings.gradle
└── src
    ├── main
    │ ├── java
    │ │ └── com
    │ │ └── hackersandslackers
    │ │ └── gradletutorial
    │ │ └── App.java
    │ └── resources
    └── test
        ├── java
        │ └── com
        │ └── hackersandslackers
        │ └── gradletutorial
        │ └── AppTest.java
        └── resources
```
<figcaption>/java-gradle-tutorial</figcaption>

Gradle has started our project by generating the barebones of our project as well as some Gradle-related configuration files. Not only does Gradle create our main module within /src, but it also creates the corresponding folder structure needed to test said module. If we were to create this project without Gradle, we would've had to create 10 directories!

Let's take a look at the files related to using Gradle in our project:

- **build.gradle** : This is the file we'll use to configure our builds. This is where we specify things like dependencies to download, tasks to run, projects to import, etc. This is the file we'll do the most work in.
- **settings.gradle** : Additional settings for our Gradle build.
- **gradlew** : `gradlew` stands for "Gradle wrapper" and is the file we'll use to execute our builds (for example: `$ ./gradlew build`). When we initialize Gradle in a project we actually decouple our project's Gradle from our system's Gradle, which means we could hand off our source to somebody who doesn't have Gradle installed and they'd still be able to build our project.

## Core Tasks for Java Projects

Before we get into any customization/configuration stuff, let's see what Gradle offers us out of the box! Typing `./gradlew tasks` in your project directory lists which **tasks** you can run in your project, such as building or running your code. Because we initialized Gradle as a Java project, we have this specific list of tasks:

```shell
$ ./gradlew tasks

------------------------------------------------------------
Tasks runnable from root project
------------------------------------------------------------

Application tasks
-----------------
run - Runs this project as a JVM application

Build tasks
-----------
assemble - Assembles the outputs of this project.
build - Assembles and tests this project.
buildDependents - Assembles and tests this project and all projects that depend on it.
buildNeeded - Assembles and tests this project and all projects it depends on.
classes - Assembles main classes.
clean - Deletes the build directory.
jar - Assembles a jar archive containing the main classes.
testClasses - Assembles test classes.

Build Setup tasks
-----------------
init - Initializes a new Gradle build.
wrapper - Generates Gradle wrapper files.

Distribution tasks
------------------
assembleDist - Assembles the main distributions
distTar - Bundles the project as a distribution.
distZip - Bundles the project as a distribution.
installDist - Installs the project as a distribution as-is.

Documentation tasks
-------------------
javadoc - Generates Javadoc API documentation for the main source code.

Verification tasks
------------------
check - Runs all checks.
test - Runs the unit tests.
```

No matter what you're building, you'll be using a handful of these tasks all the time. Here are a few of the most commonly used tasks.

- `./gradlew build` will compile your project's code into a _/build_ folder.
- `./gradlew run` will run the compiled code in your build folder.
- `./gradlew clean` will purge that build folder.
- `./gradlew test` will execute unit tests without building or running your code again.

Each of these tasks can be chained together for convenience. For example, `./gradlew clean build run` will create a new build of your Java project from scratch and then run said project.

These are just the core tasks that come standard with Java applications; we can script our own tasks as well. For that, we'll need to get deeper into configuring Gradle.

## Configuring & Customizing Gradle

Gradle is much more useful to us when we can configure it to do things like download dependencies, import other projects, and execute specific parts of our code at runtime. All of this magic is contained in a **build.gradle** file, which is usually made up of 3 major parts:

- **Tasks** are scripts Gradle can execute. The core tasks available to Java applications will suit most needs.
- **Dependencies** are third-party .jar files to fetch from a repository and package with your project.
- **Plugins** are Gradle plugins for additional functionality (we should have the `java` and `application` plugins active by default).
- **Projects** are standalone applications being packaged together in a single build. Not all Gradle builds consist of multiple projects, but multi-project builds are a big plus of Gradle.

Before we even jump into those, we need to tell Gradle where the main class of our application lives.

### Setting a Main Class

The first thing we should set in **build.gradle** is a variable named `mainClassName`. This is the path to our main Java class relative to the current file. Each time our build runs, Gradle will look for this class in `src/java` to start our application. It's important to note that `mainClassName` excepts the package name to be part of the path to our class, so at the very least we need our `mainClassName` to contain `[PACKAGE_NAME].[CLASS_NAME]`.  Here's my main class name:

```groovy
mainClassName="com.hackersandslackers.gradletutorial.App"
```

So my class is named `Main` and lives in a package called `com.hackersandslackers.gradletutorial`. If you recall the folder structure that Gradle created for us, the inside of **src** reflects this:

```shell
/src
└── main
    └── java
        └── com
            └── hackersandslackers
                └── gradletutorial
                    └── App.java
```
<figcaption>/src directory</figcaption>

Lastly, make sure you set your package name in your Main.java file:

```java
package com.hackersandslackers.gradletutorial

public class App {
    public String getGreeting() {
        return "Hello world.";
    }

    public static void main(String[] args) {
        System.out.println(new App().getGreeting());
    }
}
```
<figcaption>Main.java</figcaption>
### Gradle Plugins

Gradle provides us with a few "core" plugins out-of-the-box to build Java applications. Unsurprisingly, the two we have are named "Java" and "Application":

```groovy
plugins {
    id 'java'
    id 'application'
}

apply plugin:'application'
```
<figcaption>build.gradle</figcaption>

To understand what these two plugins do, try deleting them from your **build.gradle** file and running the same `./gradlew tasks` command we ran earlier:

```shell
$ ./gradlew tasks

------------------------------------------------------------
Tasks runnable from root project
------------------------------------------------------------

Build Setup tasks
-----------------
init - Initializes a new Gradle build.
wrapper - Generates Gradle wrapper files.
```

All our tasks are gone! The `java` and `application` plugins actually contain basically _all_ the useful things we gained from initializing Gradle in the first place. As it turns out, Gradle is designed to be modular to the point where Gradle alone is nothing but a wrapper designed to serve as a medium for plugins and custom logic. You can add those plugins back now.

### Installing Java Dependencies

If you've built Java projects before you're already aware of what a painfully manual process this normally is. Java has no inherent **PyPi** or **npm** equivalent: people used to download .jar files  and manually place them into their project directory like a bunch of absolute savages. Gradle does a decent job of making this process easier by handling it in **build.gradle**.

First, we need to set the remote repository we want to download Java packages from. People usually use either `mavenCentral()` or `jcenter()`, it doesn't really matter:

```groovy
...

repositories {
    jcenter()
}
```
<figcaption>build.gradle</figcaption>

With our **repositories** block created, we can then specify which dependencies to download. Below I specify that I'd like my builds to include a MySQL connector, and use the JUnit testing library:

```groovy
...

repositories {
    jcenter()
}

dependencies {
    implementation 'com.google.guava:guava:26.0-jre'
    compile group: 'mysql', name: 'mysql-connector-java', version: '5.1.13'

    testImplementation 'junit:junit:4.12'
}
```
<figcaption>build.gradle</figcaption>

### Gradle Tasks

A major part of **build.gradle** is scripting custom _tasks_. Tasks are snippets that we can run directly from the command line in our project directory ( as in `./gradlew [TASK_NAME]` ). Here's a generic task that prints something:

```groovy
task('hello') {
    doLast {
        println "hello"
    }
}
```
<figcaption>build.gradle</figcaption>

The value in parenthesis is the name of our task. If our task has no specified name, the task will run during every Gradle build by default.

`doLast` is a built-in action which means that the code in this block will be executed last.  If we wanted an action to occur first, we could use `doFirst` instead.

With that knowledge, here's a task which will run during every build and print "hello" followed by "world":

```groovy
task {
    doFirst {
        println "hello"
    }
    doLast {
        println "world"
    }
}
```
<figcaption>build.gradle</figcaption>

We can also add some helpful metadata to our tasks for whoever is using the command line and wants to interact with our project. Here we add a group to our task and add some helpful description text:

```groovy
tasks.register("hello") {
    group = 'Worthless tasks'
    description = 'An utterly useless task'

    doLast {
        println 'hello'
    }
}
```
<figcaption>build.gradle</figcaption>

Now when we run `./gradlew tasks` in our project directory, they'll be able to see the following:

```shell
$ gradle tasks

Worthless tasks
-------------
hello - An utterly useless task
```

### Multi-project Builds

The last thing worth looking at in Gradle is packaging multiple projects at once. In these types of setups, our top-level directory would contain multiple projects within it:

```shell
├── project1/
├── project2/
├── build.gradle
└── settings.gradle
```

With two project directories, we can now import each project into our top-level **settings.gradle** file:

```groovy
rootProject.name = 'java-gradle-tutorial'

include 'project1', 'project2'
```
<figcaption>settings.gradle</figcaption>

Nested projects in multi-project builds can each have their own standalone Gradle configurations with unique tasks and dependencies!