Building Java Libraries
This guide walks you through the process of using Gradle’s Build Init plugin to produce a JVM library which is suitable for consumption by other JVM libraries and applications.
What you’ll build
You’ll generate a Java library with the standard layout.
What you’ll need
- About 11 minutes
- A text editor or IDE
- A Java Development Kit (JDK), version 1.7 or better
- A Gradle distribution, version 4.6 or better
Create a library project
Gradle comes with a built-in plugin called the Build Init plugin. It is documented in the Gradle User Manual. The plugin has one task, called
init
, that generates the project. The init
task calls the (also built-in) wrapper
task to create a Gradle wrapper script, gradlew
.
The first step is to create a folder for the new project and change directory into it.
$ mkdir building-java-libraries $ cd building-java-libraries
Run the init task
From inside the new project directory, run the
init
task with the java-library
argument.$ gradle init --type java-library :wrapper :init BUILD SUCCESSFUL in 5s 2 actionable tasks: 2 executed
The
init
task runs the wrapper
task first, which generates the gradlew
and gradlew.bat
wrapper scripts. Then it creates the new project with the following structure:. ├── build.gradle ├── gradle │ └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── src ├── main │ └── java │ └── Library.java └── test └── java └── LibraryTest.java
Generated folder for wrapper files | |
Default Java source folder | |
Default Java test folder |
You now have the necessary components for a simple Java library project.
Review the generated project files
The
settings.gradle
file is heavily commented, but has only one active line:
Generated settings.gradle
rootProject.name='building-java-libraries'
This assigns the name of the root project. |
The generated
build.gradle
file also has many comments. The active portion is reproduced here (note version numbers for the dependencies may be updated in later versions of Gradle):
Generated build.gradle
plugins {
id 'java-library'
}
dependencies {
api 'org.apache.commons:commons-math3:3.6.1'
implementation 'com.google.guava:guava:23.0'
testImplementation 'junit:junit:4.12'
}
repositories {
jcenter()
}
version = '0.1.0'
jar {
manifest {
attributes('Implementation-Title': project.name,
'Implementation-Version': project.version)
}
}
Public Bintray Artifactory repository | |
This is an example of a dependency which is exported to consumers, that is to say found on their compile classpath. | |
This is an exampe of a dependency which is used internally, and not exposed to consumers on their own compile classpath. | |
JUnit testing library |
The
build.gradle
adds the java-library plugin. This is an extension of the java-base
plugin and adds additional tasks for compiling Java source code.
The file
src/main/java/Library.java
is shown here:
Generated src/main/java/Library.java
public class Library {
public boolean someLibraryMethod() {
return true;
}
}
The generated JUnit specification,
src/test/java/LibraryTest.java
is shown next:
Generated src/test/java/LibraryTest.java
import org.junit.Test;
public class LibraryTest {
@Test public void testSomeLibraryMethod() {
Library classUnderTest = new Library();
assertTrue("someLibraryMethod should return 'true'", classUnderTest.someLibraryMethod());
}
}
The generated test class has a single JUnit 4 test. The test instantiates the
Library
class, invokes the someLibraryMethod
method, and checks that the returned value is `true.Assemble the library JAR
To build the project, run the
build
task. You can use the regular gradle
command, but when a project includes a wrapper script, it is considered good form to use it instead.$ ./gradlew build :compileJava :processResources NO-SOURCE :classes :jar :assemble :compileTestJava :processTestResources NO-SOURCE :testClasses :test :check :build BUILD SUCCESSFUL in 9s 4 actionable tasks: 4 executed
The first time you run the wrapper script, gradlew , there may be a delay while that version of gradle is downloaded and stored locally in your ~/.gradle/wrapper/dists folder. |
The first time you run the build, Gradle will check whether or not you already have the JUnit libraries and other listed dependencies in your cache under your
~/.gradle
directory. If not, the libraries will be downloaded and stored there. The next time you run the build, the cached versions will be used. The build
task compiles the classes, runs the tests, and generates a test report.
You can view the test report by opening the HTML output file, located at
build/reports/tests/test/index.html
.
A sample report is shown here:
You can find your newly packaged JAR file in the
build/libs
directory with the name building-java-libraries.jar
. Verify that the archive is valid by running the following command:$ jar tf build/libs/building-java-libraries.jar META-INF/ META-INF/MANIFEST.MF Library.class
You should see the required manifest file—
MANIFEST.MF
—and the compiled Library
class.
All of this happens without any additional configuration in
build.gradle because Gradle’s java-library plugin assumes your project sources are arranged in a conventional project layout. You can customize the project layout if you wish as described in the user manual. |
Congratulations, you have just completed the first step of creating a Java library! You can can now customize this to your own project needs.
Customize the library JAR
You will often want the name of the JAR file to include the library version. This is easily achieved by setting a top-level
version
property in the build script, like so:
build.gradle
version = '0.1.0'
Now run the
jar
task:$ ./gradlew jar
and notice that the resulting JAR file at
build/libs/building-java-libraries-0.1.0.jar
contains the version as expected.
Another common requirement is customizing the manifest file, typically by adding one or more attributes. Let’s include the library name and version in the manifest file by configuring the
jar
task. Add the following to the end of your build script:
build.gradle
jar {
manifest {
attributes('Implementation-Title': project.name,
'Implementation-Version': project.version)
}
}
To confirm that these changes work as expected, run the
jar
task again, and this time also unpack the manifest file from the JAR:$ ./gradlew jar $ jar xf build/libs/building-java-libraries-0.1.0.jar META-INF/MANIFEST.MF
Now view the contents of the
META-INF/MANIFEST.MF
file and you should see the following:
META-INF/MANIFEST.MF
Manifest-Version: 1.0
Implementation-Title: building-java-libraries
Implementation-Version: 0.1.0
Learn more about configuring JARs
The
manifest is just one of many properties that can be configured on the jar task. For a complete list, see the Jar section of the Gradle Language Reference as well as the Jar and Creating Archives sections of the Gradle User Manual. |
Now you can complete this exercise by trying to compile some Java code that uses the library you just built.
Adding API documentation
The
java-library
plugin has built-in support for Java’s API documentation tool via the javadoc
task.
The code generated by the Build Init plugin already placed a comment on the
Library.java
file. Modify the comment to become javadoc
markup.
src/main/java/Library.java
/** This java source file was generated by the Gradle 'init' task.
*/
public class Library {
public boolean someLibraryMethod() {
return true;
}
}
Run the
javadoc
task.$ ./gradlew javadoc :compileJava :processResources NO-SOURCE :classes :javadoc BUILD SUCCESSFUL in 1s 2 actionable tasks: 2 executed
You can view the generated
javadoc
files by opening the HTML file located at build/docs/javadoc/index.html
.Summary
That’s it! You’ve now successfully built a Java library project, packaged it as a JAR and consumed it within a separate application. Along the way, you’ve learned how to:
- Generate a Java library
- Adapt the generated
build.gradle
and sample Java files are structured - Run the build and view the test report
- Customize the name of a JAR file and the content of its manifest
- Generate API documentation.
No comments:
Post a Comment