
The fabulous JCrete unconference ends with a “hack day”. I never thought I could contribute anything useful in a few hours, but this year Maurice Naftalin and myself succeeded. Version 3.6.0 of the exec-maven-plugin contains the fruit of our labor. Now you can use Maven to execute an “instance main method”.
This is one of my favorite features of Java 25. You can now write a “Hello, World” program as follows:
void main() {
System.out.println("Hello, World!);
}
You can actually do a bit better, and use IO.println, but that’s unrelated to this topic, so I won’t dwell on it.
No class declaration. When you place the code in a file HelloWorld.java, a class HelloWorld is automatically inferred. That’s the “compact source files” feature of JEP 512, and it is also tangential to the exec-maven-plugin. The maven-compiler-plugin can deal with that just fine.
The point is void main, without static and without a String[] parameter.

JEP 512 states that any of four forms of main can be used to launch a Java program. Such a method is called a launchable method.
main methods with a String[] parameter are preferred, and main methods with no parameters are also ok.static, main is invoked, with or without command-line arguments. Otherwise, the class must have an non-private no-arg constructor. It is invoked, and then main is invoked on the constructed object. Again, with or without command-line arguments.Who cares? After all, we aren’t actively launching the main method of Spring, Quarkus, or whatever our favorite framework is. This matters to two categories of users:

public static void main(String[] args)Would either of these two groups of users actually want to use Maven to launch their program?
Maybe not, but this style might become more popular in general. Also, I have another marginal use case. Reviewers of the 14th edition of Core Java asked for a Maven project that would compile all of the sample programs at once. (There are over two hundred of them.)
The reviewers were keen on the Maven project so that their preferred IDE could suck in all of the sample programs at once. (Thanks to Heinz Kabutz for actually making that happen!)
I was a bit miffed, though, that one couldn’t use the Maven project to execute those sample projects, which I had mostly changed to the easier JEP 512 forms.

The exec-maven-plugin wanted a static void main(String[] args). Seeking to escape that bed of Procrustes, it was also willing to execute the run method of a Runnable.
That’s where Maurice and myself came in. Let’s extend it to the other forms allowed by JEP 512, I said. As a motivational device, I told him that all important JEPs are powers of 2. (He was dubious, and rightly so.)
The implementation is straightforward. It just follows the JEP.
Now the exec-maven-plugin can execute all four forms of the main method, following the rules of JEP 512. (And it can still execute a Runnable.)
It is easy enough to do. Here is a POM file for a simple project:
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.horstmann.corejava</groupId>
<artifactId>corejava-bookcode</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>25</maven.compiler.source>
<maven.compiler.target>25</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<build>
<sourceDirectory>.</sourceDirectory>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.14.0</version>
<configuration>
<release>${maven.compiler.source}</release>
<fork>true</fork>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.6.0</version>
<executions>
<execution>
<goals>
<goal>exec</goal>
</goals>
</execution>
</executions>
<configuration>
<executable>maven</executable>
</configuration>
</plugin>
</plugins>
</build>
</project>
In the same directory as the POM file (and not a distant subdirectory src/main/java), place a file HelloWorld.java:
void main() {
System.out.println("Hello, World!);
}
Then run
mvn install
and
mvn exec:java -Dexec.mainClass=HelloWorld
You are rewarded with:
[INFO] Scanning for projects... [INFO] [INFO] --------------< com.horstmann.corejava:corejava-bookcode >-------------- [INFO] Building corejava-bookcode 1.0-SNAPSHOT [INFO] from pom.xml [INFO] --------------------------------[ jar ]--------------------------------- [INFO] [INFO] --- exec:3.6.0:java (default-cli) @ corejava-bookcode --- [INFO] [stdout] Hello, World! [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 0.883 s [INFO] Finished at: 2025-09-30T20:11:26+02:00 [INFO] -----------------------------------------------------------------------

That’s nice. For a Maven user.
Ok, it might not warm the hearts and minds of the readers of Core Java, and I am not pushing it on them. In the book, I strictly stick to command-line tools:
java HelloWorld.java
Note the absence of a compilation step, thanks to JEP 330 and JEP 458.
Since there is a Maven plugin for launching Java programs, it might as well follow the Java rules for what classes are launchabe. And now it is updated to the latest rules.
The plugin even works for programs prior to Java 25.
Just another small step for open source in action. I learned a lesson about the efficacy of hack events.

By the way, for me, the most impressive hack at that JCrete event was from Marc Hoffmann. He managed to run Java 1.0 in the browser, inside a JavaScript emulator of Windows 95. Nervous text could never be more beautiful.
With a Mastodon account (or any account on the fediverse), please visit this link to add a comment.
Thanks to Carl Schwann for the code for loading the comments.
Not on the fediverse yet? Comment below with Talkyard.