Running a Java Maven Project from the Command Line(CLI) - With Example Code
This tutorial will show you how to run a basic Java Maven project on the command line (as opposed to on an IDE).
We will learn how to:
- Create a new maven project
- Compile and execute your Java code into an executable JAR file
- Add unit test for your code
All of these tasks will be done on the command line, so that you can have a better idea on whatβs going on under the hood, and how you can run a java application in environments that donβt have a full-featured IDE like Eclipse or IntelliJ.
If you just want to see the example code, you can view it on Github
Creating a New Maven Project
If you havenβt already, install OpenJDK on your system, after which you can install maven.
First, letβs create a new project folder using the maven command line tools:
mvn -B archetype:generate -DgroupId=com.sohamkamani -DartifactId=mvn-example -DarchetypeArtifactId=maven-archetype-quickstart -DarchetypeVersion=1.4
Iβve used a groupID corresponding to my domain (sohamkamani.com), you should replace this with your own choice
This generates a new project folder with the following structure:
mvn-example
βββ pom.xml
βββ src
βββ main
β βββ java
β βββ com
β βββ sohamkamani
β βββ App.java
βββ test
βββ java
βββ com
βββ sohamkamani
βββ AppTest.java
App.java
contains simple code that prints Hello World!
when run.
Compiling our JAR File
Before running a Java application in production, weβll need to compile the Java code into byte-code that can be run on the JVM.
If we have multiple classes and folder (which we most likely will), we have to package the compiled code into a common format (like a .jar
file).
We can perform compilation and packaging by running the following command:
mvn compile
mvn package
We can combine these two commands by running
mvn compile package
Running these commands will create a bunch of files in a new target
directory:
mvn-example
βββ pom.xml
βββ src/...
βββ target
βββ classes
β βββ com
β βββ sohamkamani
β βββ App.class
βββ test-classes
β βββ com
β βββ sohamkamani
β βββ AppTest.class
βββ mvn-example-1.0-SNAPSHOT.jar
Some auxillary files are omitted from here for the sake of clarity
The JAR file is the final output that can be executed by the JVM. However, we still have to perform some additional steps before we can run our code.
Running our Code
We can use the java
command to execute our JAR file:
java -jar target/mvn-example-1.0-SNAPSHOT.jar
If we run this now, we will get the following error:
no main manifest attribute, in target/tmp-mvn-example-1.0-SNAPSHOT.jar
This is because the JAR file doesnβt know the entry point, so it has no idea where the main method is.
We can make use of the Maven JAR plugin, which gives us additional capabilities to build JAR files.
We can add the following configuration as a child of the <build>
tag:
<!-- this goes within <build> -->
<plugins>
<plugin>
<!-- Build an executable JAR -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<!-- here we specify that we want to use the main method within the App class -->
<mainClass>com.sohamkamani.App</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
</plugins>
<!-- other properties -->
We can now rebuild the project by running:
mvn clean compile package
The
clean
subcommand removes previous artifacts in the target directory, such as the previous stale JAR file
Next, we can execute the JAR file by running:
java -jar target/mvn-example-1.0-SNAPSHOT.jar
Which will give us the output:
Hello World!
Running Unit Tests
Maven can also be used to run tests that weβve defined in our project.
By convention, all tests reside within the src/test
directory.
For the purpose of illustration, letβs create a static method to add two numbers in the App
class:
public class App {
public static void main(String[] args) {
System.out.printf("Hello World! %d", Calculator.add(4, 5));
}
public static int add(int n1, int n2) {
return n1 + n2;
}
}
We can now create a unit test for this method within src/test/java/com/sohamkamani/AppTest.java
:
package com.sohamkamani;
// the JUnit library is used for testing
import static org.junit.Assert.assertEquals;
import org.junit.Test;
public class AppTest {
@Test
public void shouldAnswerWithTrue() {
int result = App.add(3, 4);
assertEquals(7, result);
}
}
By default, the maven project folder comes bundled with the JUnit library for running unit tests
To run tests, we can run the mvn test
command - this will run all tests, tell us how many passed and failed, and give us more information about the failed tests.
Adding Dependencies with the Maven Assembly Plugin
Letβs look at how to add dependencies and package them in our JAR file.
For most applications need external libraries (like Spring Boot or Apache Commons) to implement common functionality. Maven allows us to install these dependencies by specifying them in our pom.xml
file.
For this example, letβs install the Cowsay library, which will display our output as a quote from a friendly cartoon figure of a cow.
First, we have to add Cowsay as a dependency in our pom.xml
file:
<!-- ... -->
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<!-- We can add additional dependencies here -->
<dependency>
<groupId>com.github.ricksbrown</groupId>
<artifactId>cowsay</artifactId>
<version>1.1.0</version>
<classifier>lib</classifier>
</dependency>
</dependencies>
<!-- ... -->
Next, we can use the Cowsay.say
method within our main
method to print the final output string:
package com.sohamkamani;
// import Cowsay class from the library
import com.github.ricksbrown.cowsay.Cowsay;
public class App {
public static void main(String[] args) {
int result = Calculator.add(4, 5);
// We can specify the arguments and get the display
// string from the `Cowsay.say` method
String[] cowArgs = new String[] { String.valueOf(result) };
String cowString = Cowsay.say(cowArgs);
// print the final output string
System.out.printf(cowString);
}
public static int add(int n1, int n2) {
return n1 + n2;
}
}
However, thereβs a problem - If we recompile our code and try to run the app now, we will get an error:
$ mvn clean compile package
$ java -jar target/mvn-example-1.0-SNAPSHOT.jar
Exception in thread "main" java.lang.NoClassDefFoundError: com/github/ricksbrown/cowsay/Cowsay
at com.sohamkamani.App.main(App.java:13)
Caused by: java.lang.ClassNotFoundException: com.github.ricksbrown.cowsay.Cowsay
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:520)
... 1 more
It looks like the Java class loader couldnβt find the classes for the Cowsay
library, even though we added it as a dependency in the pom.xml
file.
This happens because by default, maven doesnβt bundle the dependency class files along with the application code. To enable this, we can use the maven-assembly-plugin.
This plugin includes all of our applications dependencies into the JAR file. This increases its overall size, but ensures that we can run it as a standalone executable using the java -jar
command.
Letβs add the Maven assembly plugin in the pom.xml
build definition:
<!-- ... -->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<mainClass>com.sohamkamani.App</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
<!-- Add the assemble plugin with standard configuration -->
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<archive>
<manifest>
<mainClass>com.sohamkamani.App</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</plugin>
</plugins>
<!-- ... -->
</build>
<!-- ... -->
To compile our code, we can run the assembly:single
goal in place of the package
goal:
mvn clean compile assembly:single
This creates a new JAR file in the target directory that you can run using the java -jar
command:
$ java -jar target/mvn-example-1.0-SNAPSHOT-jar-with-dependencies.jar
___
< 9 >
---
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
You can view the complete example code for this post on Github
How do you organize your Java projects? Do you ever use the command line for common tasks? Let me know in the comments!