Monday, May 02, 2022

Maven - running Java app . . .

When Maven is the build tool and you have no choice, you need to factor in additional amount of development time dealing with its build file: the pom.xml. Any new additional feature eats up unexpected time.

Environment: Java 17, maven 3.8.5 on macOS Catalina 10.15.7

Recently, I had to work on a task that involved writing a new Plain Old Java Application (POJA) with a main method. This application is new addition to the existing half-a-dozen Java applications family residing in a Maven module in a multi-module project. Each application is a kind of ETL app (Extract, Transform, and Load data) with XML, Excel spread-sheets as data sources. The transformed output is Flyway SQL script. Sounds simple! But, not really!!

The existing applications family has a strong contract with their parent class through Inheritance (OO model) & Copy-and-paste (popular dev-model) for sharing code, inherit about 50 final and non-final Static constants  Also, every application's specific run requires changing constant values for every run and checkin latest code changes. Huh, old messy way of maintaining apps. Joining the legacy family, following the inheritance model (to keep the family relationship), at least I wanted to 1) Eliminate Copy-and-paste sharing model 2) Add CLI support to pass in run specific values for constants.

In Groovy world, no specific library is needed for developing Java apps with CLI support. It comes with CliBuilder. For Java, googling found me Picocli. But Maven got in the way giving bit hard time for executing the application. After going through some stackoverflow explanations and my experiments, learnt that two ways of running the app is possible. Both require to leverage the plugin: Exec Maven Plugin.

1. Configure the plugin - if there are multiple applications, multiple executions can be setup. 

... <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>exec-maven-plugin</artifactId> <version>1.4.0</version> <executions> <!-- mvn compile exec:java@my-etl-app --> <execution> <id>my-etl-app</id> <goals> <goal>java</goal> </goals> <configuration> <mainClass>com.giri.etl.MyEtlAppPicoCli</mainClass> </configuration> </execution> <!-- mvn compile exec:java@my-etl-app-new --> <execution> <id>my-etl-app-new</id> <goals> <goal>java</goal> </goals> <configuration> <mainClass>com.giri.etl.MyEtlAppNewPicoCli</mainClass> </configuration> </execution> </executions> </plugin> </plugins> </build>

With this the application can be run by specifying the goal exec:java like:
mvn compile exec:java@my-etl-app
 
assuming that the code is compiled and the other dependent modules are built.

2.  Configure the plugin in Profiles - if there are multiple applications, multiple profiles can be setup. 

... <profiles> <!-- mvn exec:java -Pmy-etl-app --> <profile> <id>my-etl-app</id> <build> <plugins> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>exec-maven-plugin</artifactId> <version>1.4.0</version> <configuration> <mainClass>com.giri.etl.MyEtlAppPicoCli</mainClass> </configuration> </plugin> </plugins> </build> </profile> <!-- mvn exec:java -Pmy-etl-app-new --> <profile> <id>my-etl-app-new</id> <build> <plugins> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>exec-maven-plugin</artifactId> <version>1.4.0</version> <configuration> <mainClass>com.giri.etl.MyEtlAppNewPicoCli</mainClass> </configuration> </plugin> </plugins> </build> </profile> </profiles> </project>

With this the application can be run by specifying the goal exec:java like:
mvn compile exec:java -Pmy-etl-app
assuming that the code is compiled and the other dependent modules are built.

TIP-1

There is also a standard way of specifying the main application class directly like:
mvn compile exec:java -Dexec.mainClass="com.giri.etl.MyEtlAppPicoCli" -Dexec.args="-y=2022"

To pass application arguments to the Java app when running the maven goal: exec:java, use -Dexec.args like: 
mvn compile exec:java -Pmy-etl-app -Dexec.args="-<arg1>=<value1> -<arg2>=<value2>"

E.g. 
mvn exec :java -Pmy-etl-app -Dexec.args="-y=2022 -run-extra-checks=false"
Assuming that -y, -run-extra-checks are specific CLI arguments added to my app by leveraging Picocli Framework.

TIP-2

Java Preview Feature maven support (both compile-time, and run-time)

Java preview features are disabled by default. If any of the preview features are used in the code, it must be enabled explicitly by using the command line option --enable-preview for both compiler and runtime.

Compile-time
For compile-time support in maven builds, the maven-compiler-plugin must be configured to pass the compiler flag as shown below:

</build> <plugins> ... <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.7.0</version> <configuration> <release>${jdk.version}</release> <source>${jdk.version}</source> <target>${jdk.version}</target> <compilerArgs> <arg>-Xlint:all</arg> <arg>--enable-preview</arg> </compilerArgs> </configuration> </plugin> </plugins> </build>

Run-time
With Exec Maven Plugin used for running the Java application, the run-time preview features enabling is also necessary. The above maven-compiler-plugin configuration is only good for compilation. For runtime system in this scenario, a jvm.config file needs to exist in the .mvn folder of the project where you are running the application from.
The content of this file should be just:
--enable-preview

References

No comments:

Post a Comment